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