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