1 /* Copyright © Chris Le Sueur and Robby Griffin, 2005-2006
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:
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
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.
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.
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
38 #include "screenhack.h"
41 #define countof(x) (sizeof((x)) / sizeof(*(x)))
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 */
49 int ua, ub, utx; /* Precomputed combined r,s,t values */
50 int uc, ud, uty; /* Precomputed combined r,s,t values */
62 int blackColor, whiteColor;
64 int width, widthb, height;
67 XPoint pointbuf[1000];
69 int xmin, xmax, ymin, ymax;
80 Bool translate, scale, rotate;
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)))
89 return (((float)random() / RAND_MAX) * up);
92 static const char *ifs_defaults [] = {
104 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
105 "*doubleBuffer: False",
107 "*doubleBuffer: True",
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" },
131 /* Draw all the queued points on the backbuffer */
133 drawpoints(struct state *st)
135 XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
140 /* Set a point to be drawn, if it hasn't been already.
141 * Expects coordinates in 256ths of a pixel. */
143 sp(struct state *st, int x, int y)
145 if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
151 if (getdot(x, y)) return;
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;
159 st->pointbuf[st->npoints].x = x;
160 st->pointbuf[st->npoints].y = y;
163 if (st->npoints >= countof(st->pointbuf)) {
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|
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.
179 lensmatrix(struct state *st, Lens *l)
181 l->ua = 1024.0 * l->s * cos(l->r);
182 l->ub = -1024.0 * l->s * sin(l->r);
185 l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
187 l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
192 CreateLens(struct state *st,
199 newlens->sa = newlens->txa = newlens->tya = 0;
201 newlens->r = newlens->ro = newlens->rt = nr;
207 newlens->s = newlens->so = newlens->st = ns;
210 else newlens->s = 0.5;
215 lensmatrix(st, newlens);
219 mutate(struct state *st, Lens *l)
226 l->rt = myrandom(4) - 2;
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;
235 /* Reset counter, obtain new target value */
238 l->st = myrandom(2) - 1;
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;
248 l->txa += myrandom(0.004) - 0.002;
249 l->tya += myrandom(0.004) - 0.002;
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;
259 if (st->rotate || st->scale || st->translate) {
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)*/
269 /* Calls itself <lensnum> times - with results from each lens/function. *
270 * After <length> calls to itself, it stops iterating and draws a point. */
272 recurse(struct state *st, int x, int y, int length, int p)
282 sp(st, STEPX(l, x, y), STEPY(l, x, y));
286 for (i = 0; i < st->lensnum; i++) {
288 recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
293 /* Performs <count> random lens transformations, drawing a point at each
294 * iteration after the first 10.
297 iterate(struct state *st, int count, int p)
306 l = &st->lenses[random() % st->lensnum]; \
307 tx = STEPX(l, x, y); \
308 y = STEPY(l, x, y); \
311 for (i = 0; i < 10; i++) {
315 for ( ; i < count; i++) {
322 sp(st, STEPX(l, x, y), STEPY(l, x, y));
332 /* Come on and iterate, iterate, iterate and sing... *
333 * Yeah, this function just calls iterate, mutate, *
334 * and then draws everything. */
336 ifs_draw (Display *dpy, Window window, void *closure)
338 struct state *st = (struct state *) closure;
340 int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
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,
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;
356 st->ccolour %= st->ncolours;
358 /* calculate and draw points for this frame */
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));
369 recurse(st, x, y, st->length - 1, i);
371 iterate(st, pow(st->lensnum, st->length - 1), i);
378 XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
379 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
381 recurse(st, x, y, st->length, 0);
383 iterate(st, pow(st->lensnum, st->length), 0);
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,
399 xmax - xmin + 1, ymax - ymin + 1,
403 for(i = 0; i < st->lensnum; i++) {
404 mutate(st, &st->lenses[i]);
411 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
414 ifs_init (Display *d_arg, Window w_arg)
416 struct state *st = (struct state *) calloc (1, sizeof(*st));
418 XWindowAttributes xgwa;
420 /* Initialise all this X shizzle */
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);
428 XGetWindowAttributes (st->dpy, st->window, &xgwa);
429 ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
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,
441 /* Initialize IFS data */
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");
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");
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++) {
471 ifs_reshape (Display *dpy, Window window, void *closure,
472 unsigned int w, unsigned int h)
474 struct state *st = (struct state *)closure;
475 XWindowAttributes xgwa;
477 /* oh well, we need the screen depth anyway */
478 XGetWindowAttributes (st->dpy, st->window, &xgwa);
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;
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;
492 if (st->backbuffer != None && st->backbuffer != st->window) {
493 XFreePixmap(st->dpy, st->backbuffer);
494 st->backbuffer = None;
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);
503 st->backbuffer = st->window;
504 XClearWindow(st->dpy, st->window);
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);
513 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
519 ifs_free (Display *dpy, Window window, void *closure)
521 struct state *st = (struct state *) closure;
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);
531 XSCREENSAVER_MODULE ("IFS", ifs)