From http://www.jwz.org/xscreensaver/xscreensaver-5.38.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   ".lowrez:             true",
94   ".background:         Black",
95   "*lensnum:            3",
96   "*fpsSolid:           true",
97   "*length:             9",
98   "*mode:               0",
99   "*colors:             200",
100   "*delay:              20000",
101   "*translate:          True",
102   "*scale:              True",
103   "*rotate:             True",
104   "*recurse:            False",
105   "*multi:              True",
106 # ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
107   "*doubleBuffer:       False",
108 #else
109   "*doubleBuffer:       True",
110 #endif
111 #ifdef HAVE_MOBILE
112   "*ignoreRotation:     True",
113 #endif
114   0
115 };
116
117 static XrmOptionDescRec ifs_options [] = {
118   { "-detail",          ".length",      XrmoptionSepArg, 0 },
119   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
120   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
121   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
122   { "-functions",       ".lensnum",     XrmoptionSepArg, 0 },
123   { "-no-translate",    ".translate",   XrmoptionNoArg, "False" },
124   { "-no-scale",        ".scale",       XrmoptionNoArg, "False" },
125   { "-no-rotate",       ".rotate",      XrmoptionNoArg, "False" },
126   { "-recurse",         ".recurse",     XrmoptionNoArg, "True" },
127   { "-iterate",         ".recurse",     XrmoptionNoArg, "False" },
128   { "-multi",           ".multi",       XrmoptionNoArg, "True" },
129   { "-no-multi",        ".multi",       XrmoptionNoArg, "False" },
130   { "-db",              ".doubleBuffer",XrmoptionNoArg, "True" },
131   { "-no-db",           ".doubleBuffer",XrmoptionNoArg, "False" },
132   { 0, 0, 0, 0 }
133 };
134
135
136 /* Draw all the queued points on the backbuffer */
137 static void
138 drawpoints(struct state *st)
139 {
140   XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
141               CoordModeOrigin);
142   st->npoints = 0;
143 }
144
145 /* Set a point to be drawn, if it hasn't been already.
146  * Expects coordinates in 256ths of a pixel. */
147 static void
148 sp(struct state *st, int x, int y)
149 {
150   if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
151     return;
152
153   x >>= 8;
154   y >>= 8;
155
156   if (getdot(x, y)) return;
157   setdot(x, y);
158
159   if (x < st->xmin) st->xmin = x;
160   if (x > st->xmax) st->xmax = x;
161   if (y < st->ymin) st->ymin = y;
162   if (y > st->ymax) st->ymax = y;
163
164   st->pointbuf[st->npoints].x = x;
165   st->pointbuf[st->npoints].y = y;
166   st->npoints++;
167
168   if (st->npoints >= countof(st->pointbuf)) {
169     drawpoints(st);
170   }
171 }
172
173
174 /* Precompute integer values for matrix multiplication and vector
175  * addition. The matrix multiplication will go like this (see iterate()):
176  *   |x2|     |ua ub|   |x|     |utx|
177  *   |  |  =  |     | * | |  +  |   |
178  *   |y2|     |uc ud|   |y|     |uty|
179  * 
180  * There is an extra factor of 2^10 in these values, and an extra factor of
181  * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
182  */
183 static void
184 lensmatrix(struct state *st, Lens *l)
185 {
186   l->ua = 1024.0 * l->s * cos(l->r);
187   l->ub = -1024.0 * l->s * sin(l->r);
188   l->uc = -l->ub;
189   l->ud = l->ua;
190   l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
191                                    + l->tx / 16 + 1);
192   l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
193                                      + l->ty / 16 - 1);
194 }
195
196 static void
197 CreateLens(struct state *st,
198            float nr,
199            float ns,
200            float nx,
201            float ny,
202            Lens *newlens)
203 {
204   newlens->sa = newlens->txa = newlens->tya = 0;
205   if (st->rotate) {
206     newlens->r = newlens->ro = newlens->rt = nr;
207     newlens->rc = 1;
208   }
209   else newlens->r = 0;
210
211   if (st->scale) {
212     newlens->s = newlens->so = newlens->st = ns;
213     newlens->sc = 1;
214   }
215   else newlens->s = 0.5;
216
217   newlens->tx = nx;
218   newlens->ty = ny;
219
220   lensmatrix(st, newlens);
221 }
222         
223 static void
224 mutate(struct state *st, Lens *l)
225 {
226   if (st->rotate) {
227     float factor;
228     if(l->rc >= 1) {
229       l->rc = 0;
230       l->ro = l->rt;
231       l->rt = myrandom(4) - 2;
232     }
233     factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
234     l->r = l->ro + (l->rt - l->ro) * factor;
235     l->rc += 0.01;
236   }
237   if (st->scale) {
238     float factor;
239     if (l->sc >= 1) {
240       /* Reset counter, obtain new target value */
241       l->sc = 0;
242       l->so = l->st;
243       l->st = myrandom(2) - 1;
244     }
245     factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
246     /* Take average of old target and new target, using factor to *
247      * weight. It's computed sinusoidally, resulting in smooth,   *
248      * rhythmic transitions.                                      */
249     l->s = l->so + (l->st - l->so) * factor;
250     l->sc += 0.01;
251   }
252   if (st->translate) {
253     l->txa += myrandom(0.004) - 0.002;
254     l->tya += myrandom(0.004) - 0.002;
255     l->tx += l->txa;
256     l->ty += l->tya;
257     if (l->tx > 6) l->txa -= 0.004;
258     if (l->ty > 6) l->tya -= 0.004;
259     if (l->tx < -6) l->txa += 0.004;
260     if (l->ty < -6) l->tya += 0.004;
261     if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
262     if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
263   }
264   if (st->rotate || st->scale || st->translate) {
265     lensmatrix(st, l);
266   }
267 }
268
269
270 #define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
271 #define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
272 /*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
273
274 /* Calls itself <lensnum> times - with results from each lens/function.  *
275  * After <length> calls to itself, it stops iterating and draws a point. */
276 static void
277 recurse(struct state *st, int x, int y, int length, int p)
278 {
279   int i;
280   Lens *l;
281
282   if (length == 0) {
283     if (p == 0) 
284       sp(st, x, y);
285     else {
286       l = &st->lenses[p];
287       sp(st, STEPX(l, x, y), STEPY(l, x, y));
288     }
289   }
290   else {
291     for (i = 0; i < st->lensnum; i++) {
292       l = &st->lenses[i];
293       recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
294     }
295   }
296 }
297
298 /* Performs <count> random lens transformations, drawing a point at each
299  * iteration after the first 10.
300  */
301 static void
302 iterate(struct state *st, int count, int p)
303 {
304   int i;
305   Lens *l;
306   int x = st->x;
307   int y = st->y;
308   int tx;
309
310 # define STEP()                              \
311     l = &st->lenses[random() % st->lensnum]; \
312     tx = STEPX(l, x, y);                     \
313     y = STEPY(l, x, y);                      \
314     x = tx
315
316   for (i = 0; i < 10; i++) {
317     STEP();
318   }
319
320   for ( ; i < count; i++) {
321     STEP();
322     if (p == 0)
323       sp(st, x, y);
324     else
325       {
326         l = &st->lenses[p];
327         sp(st, STEPX(l, x, y), STEPY(l, x, y));
328       }
329   }
330
331 # undef STEP
332
333   st->x = x;
334   st->y = y;
335 }
336
337 /* Come on and iterate, iterate, iterate and sing... *
338  * Yeah, this function just calls iterate, mutate,   *
339  * and then draws everything.                        */
340 static unsigned long
341 ifs_draw (Display *dpy, Window window, void *closure)
342 {
343   struct state *st = (struct state *) closure;
344   int i;
345   int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
346   int partcolor, x, y;
347   
348
349   /* erase whatever was drawn in the previous frame */
350   if (xmin <= xmax && ymin <= ymax) {
351     XSetForeground(st->dpy, st->gc, st->blackColor);
352     XFillRectangle(st->dpy, st->backbuffer, st->gc,
353                    xmin, ymin,
354                    xmax - xmin + 1, ymax - ymin + 1);
355     st->xmin = st->width + 1;
356     st->xmax = st->ymax = -1;
357     st->ymin = st->height + 1;
358   }
359
360   st->ccolour++;
361   st->ccolour %= st->ncolours;
362
363   /* calculate and draw points for this frame */
364   x = st->width << 7;
365   y = st->height << 7;
366   
367   if (st->multi) {
368     for (i = 0; i < st->lensnum; i++) {  
369       partcolor = st->ccolour * (i+1);
370       partcolor %= st->ncolours;
371       XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
372       memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
373       if (st->recurse)   
374         recurse(st, x, y, st->length - 1, i);
375       else
376         iterate(st, pow(st->lensnum, st->length - 1), i);
377       if (st->npoints) 
378         drawpoints(st);
379     }
380   } 
381   else {
382     
383     XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
384     memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
385     if (st->recurse)
386       recurse(st, x, y, st->length, 0);
387     else
388       iterate(st, pow(st->lensnum, st->length), 0);
389     if (st->npoints)
390       drawpoints(st);
391   }
392   
393   /* if we just drew into a buffer, copy the changed area (including
394    * erased area) to screen */
395   if (st->backbuffer != st->window
396       && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
397           || (xmin <= xmax && ymin <= ymax))) {
398     if (st->xmin < xmin) xmin = st->xmin;
399     if (st->xmax > xmax) xmax = st->xmax;
400     if (st->ymin < ymin) ymin = st->ymin;
401     if (st->ymax > ymax) ymax = st->ymax;
402     XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
403               xmin, ymin,
404               xmax - xmin + 1, ymax - ymin + 1,
405               xmin, ymin);
406   }
407
408   for(i = 0; i < st->lensnum; i++) {
409     mutate(st, &st->lenses[i]);
410   }
411
412   return st->delay;
413 }
414
415 static void
416 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
417
418 static void *
419 ifs_init (Display *d_arg, Window w_arg)
420 {
421   struct state *st = (struct state *) calloc (1, sizeof(*st));
422   int i;
423   XWindowAttributes xgwa;
424         
425   /* Initialise all this X shizzle */
426   st->dpy = d_arg;
427   st->window = w_arg;
428
429   st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
430   st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
431   st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
432
433   XGetWindowAttributes (st->dpy, st->window, &xgwa);
434   ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
435         
436   st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
437   if (st->ncolours < st->lensnum)
438     st->ncolours = st->lensnum;
439   if (st->colours) free(st->colours);
440   if (st->ncolours < 1) st->ncolours = 1;
441   st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
442   if (!st->colours) exit(1);
443   make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap, 
444                         st->colours, &st->ncolours,
445                         True, 0, False);
446
447   /* Initialize IFS data */
448  
449   st->delay = get_integer_resource(st->dpy, "delay", "Delay");
450   st->length = get_integer_resource(st->dpy, "length", "Detail");
451   if (st->length < 0) st->length = 0;
452   st->mode = get_integer_resource(st->dpy, "mode", "Mode");
453
454   st->rotate    = get_boolean_resource(st->dpy, "rotate", "Boolean");
455   st->scale     = get_boolean_resource(st->dpy, "scale", "Boolean");
456   st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
457   st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
458   st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
459
460   st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
461   if (st->lenses) free (st->lenses);
462   st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
463   if (!st->lenses) exit(1);
464   for (i = 0; i < st->lensnum; i++) {
465     CreateLens(st,
466                myrandom(1)-0.5,
467                myrandom(1),
468                myrandom(4)-2,
469                myrandom(4)+2,
470                &st->lenses[i]);
471   }
472
473   return st;
474 }
475
476 static void
477 ifs_reshape (Display *dpy, Window window, void *closure, 
478              unsigned int w, unsigned int h)
479 {
480   struct state *st = (struct state *)closure;
481   XWindowAttributes xgwa;
482
483   /* oh well, we need the screen depth anyway */
484   XGetWindowAttributes (st->dpy, st->window, &xgwa);
485
486   st->width = xgwa.width;
487   st->widthb = ((xgwa.width + 31) >> 5);
488   st->height = xgwa.height;
489   st->width8 = xgwa.width << 8;
490   st->height8 = xgwa.height << 8;
491
492   if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
493     st->xmin = xgwa.width + 1;
494     st->xmax = st->ymax = -1;
495     st->ymin = xgwa.height + 1;
496   }
497
498   if (st->backbuffer != None && st->backbuffer != st->window) {
499     XFreePixmap(st->dpy, st->backbuffer);
500     st->backbuffer = None;
501   }
502
503   if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
504     st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
505     XSetForeground(st->dpy, st->gc, st->blackColor);
506     XFillRectangle(st->dpy, st->backbuffer, st->gc,
507                    0, 0, st->width, st->height);
508   } else {
509     st->backbuffer = st->window;
510     XClearWindow(st->dpy, st->window);
511   }
512
513   if (st->board) free(st->board);
514   st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
515   if (!st->board) exit(1);
516 }
517
518 static Bool
519 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
520 {
521   struct state *st = (struct state *)closure;
522   if (screenhack_event_helper (dpy, window, event))
523     {
524       int i;
525       for (i = 0; i < st->lensnum; i++) {
526         CreateLens(st,
527                    myrandom(1)-0.5,
528                    myrandom(1),
529                    myrandom(4)-2,
530                    myrandom(4)+2,
531                    &st->lenses[i]);
532       }
533       return True;
534     }
535   return False;
536 }
537
538 static void
539 ifs_free (Display *dpy, Window window, void *closure)
540 {
541   struct state *st = (struct state *) closure;
542
543   if (st->board) free(st->board);
544   if (st->lenses) free(st->lenses);
545   if (st->colours) free(st->colours);
546   if (st->backbuffer != None && st->backbuffer != st->window)
547     XFreePixmap(st->dpy, st->backbuffer);
548   free(st);
549 }
550
551 XSCREENSAVER_MODULE ("IFS", ifs)