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 [] = {
105 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
106 "*doubleBuffer: False",
108 "*doubleBuffer: True",
113 static XrmOptionDescRec ifs_options [] = {
114 { "-detail", ".length", XrmoptionSepArg, 0 },
115 { "-delay", ".delay", XrmoptionSepArg, 0 },
116 { "-mode", ".mode", XrmoptionSepArg, 0 },
117 { "-colors", ".colors", XrmoptionSepArg, 0 },
118 { "-functions", ".lensnum", XrmoptionSepArg, 0 },
119 { "-no-translate", ".translate", XrmoptionNoArg, "False" },
120 { "-no-scale", ".scale", XrmoptionNoArg, "False" },
121 { "-no-rotate", ".rotate", XrmoptionNoArg, "False" },
122 { "-recurse", ".recurse", XrmoptionNoArg, "True" },
123 { "-iterate", ".recurse", XrmoptionNoArg, "False" },
124 { "-multi", ".multi", XrmoptionNoArg, "True" },
125 { "-no-multi", ".multi", XrmoptionNoArg, "False" },
126 { "-db", ".doubleBuffer",XrmoptionNoArg, "True" },
127 { "-no-db", ".doubleBuffer",XrmoptionNoArg, "False" },
132 /* Draw all the queued points on the backbuffer */
134 drawpoints(struct state *st)
136 XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
141 /* Set a point to be drawn, if it hasn't been already.
142 * Expects coordinates in 256ths of a pixel. */
144 sp(struct state *st, int x, int y)
146 if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
152 if (getdot(x, y)) return;
155 if (x < st->xmin) st->xmin = x;
156 if (x > st->xmax) st->xmax = x;
157 if (y < st->ymin) st->ymin = y;
158 if (y > st->ymax) st->ymax = y;
160 st->pointbuf[st->npoints].x = x;
161 st->pointbuf[st->npoints].y = y;
164 if (st->npoints >= countof(st->pointbuf)) {
170 /* Precompute integer values for matrix multiplication and vector
171 * addition. The matrix multiplication will go like this (see iterate()):
172 * |x2| |ua ub| |x| |utx|
173 * | | = | | * | | + | |
174 * |y2| |uc ud| |y| |uty|
176 * There is an extra factor of 2^10 in these values, and an extra factor of
177 * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
180 lensmatrix(struct state *st, Lens *l)
182 l->ua = 1024.0 * l->s * cos(l->r);
183 l->ub = -1024.0 * l->s * sin(l->r);
186 l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
188 l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
193 CreateLens(struct state *st,
200 newlens->sa = newlens->txa = newlens->tya = 0;
202 newlens->r = newlens->ro = newlens->rt = nr;
208 newlens->s = newlens->so = newlens->st = ns;
211 else newlens->s = 0.5;
216 lensmatrix(st, newlens);
220 mutate(struct state *st, Lens *l)
227 l->rt = myrandom(4) - 2;
229 factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
230 l->r = l->ro + (l->rt - l->ro) * factor;
236 /* Reset counter, obtain new target value */
239 l->st = myrandom(2) - 1;
241 factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
242 /* Take average of old target and new target, using factor to *
243 * weight. It's computed sinusoidally, resulting in smooth, *
244 * rhythmic transitions. */
245 l->s = l->so + (l->st - l->so) * factor;
249 l->txa += myrandom(0.004) - 0.002;
250 l->tya += myrandom(0.004) - 0.002;
253 if (l->tx > 6) l->txa -= 0.004;
254 if (l->ty > 6) l->tya -= 0.004;
255 if (l->tx < -6) l->txa += 0.004;
256 if (l->ty < -6) l->tya += 0.004;
257 if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
258 if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
260 if (st->rotate || st->scale || st->translate) {
266 #define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
267 #define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
268 /*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
270 /* Calls itself <lensnum> times - with results from each lens/function. *
271 * After <length> calls to itself, it stops iterating and draws a point. */
273 recurse(struct state *st, int x, int y, int length, int p)
283 sp(st, STEPX(l, x, y), STEPY(l, x, y));
287 for (i = 0; i < st->lensnum; i++) {
289 recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
294 /* Performs <count> random lens transformations, drawing a point at each
295 * iteration after the first 10.
298 iterate(struct state *st, int count, int p)
307 l = &st->lenses[random() % st->lensnum]; \
308 tx = STEPX(l, x, y); \
309 y = STEPY(l, x, y); \
312 for (i = 0; i < 10; i++) {
316 for ( ; i < count; i++) {
323 sp(st, STEPX(l, x, y), STEPY(l, x, y));
333 /* Come on and iterate, iterate, iterate and sing... *
334 * Yeah, this function just calls iterate, mutate, *
335 * and then draws everything. */
337 ifs_draw (Display *dpy, Window window, void *closure)
339 struct state *st = (struct state *) closure;
341 int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
345 /* erase whatever was drawn in the previous frame */
346 if (xmin <= xmax && ymin <= ymax) {
347 XSetForeground(st->dpy, st->gc, st->blackColor);
348 XFillRectangle(st->dpy, st->backbuffer, st->gc,
350 xmax - xmin + 1, ymax - ymin + 1);
351 st->xmin = st->width + 1;
352 st->xmax = st->ymax = -1;
353 st->ymin = st->height + 1;
357 st->ccolour %= st->ncolours;
359 /* calculate and draw points for this frame */
364 for (i = 0; i < st->lensnum; i++) {
365 partcolor = st->ccolour * (i+1);
366 partcolor %= st->ncolours;
367 XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
368 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
370 recurse(st, x, y, st->length - 1, i);
372 iterate(st, pow(st->lensnum, st->length - 1), i);
379 XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
380 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
382 recurse(st, x, y, st->length, 0);
384 iterate(st, pow(st->lensnum, st->length), 0);
389 /* if we just drew into a buffer, copy the changed area (including
390 * erased area) to screen */
391 if (st->backbuffer != st->window
392 && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
393 || (xmin <= xmax && ymin <= ymax))) {
394 if (st->xmin < xmin) xmin = st->xmin;
395 if (st->xmax > xmax) xmax = st->xmax;
396 if (st->ymin < ymin) ymin = st->ymin;
397 if (st->ymax > ymax) ymax = st->ymax;
398 XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
400 xmax - xmin + 1, ymax - ymin + 1,
404 for(i = 0; i < st->lensnum; i++) {
405 mutate(st, &st->lenses[i]);
412 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
415 ifs_init (Display *d_arg, Window w_arg)
417 struct state *st = (struct state *) calloc (1, sizeof(*st));
419 XWindowAttributes xgwa;
421 /* Initialise all this X shizzle */
425 st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
426 st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
427 st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
429 XGetWindowAttributes (st->dpy, st->window, &xgwa);
430 ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
432 st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
433 if (st->ncolours < st->lensnum)
434 st->ncolours = st->lensnum;
435 if (st->colours) free(st->colours);
436 st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
437 if (!st->colours) exit(1);
438 make_smooth_colormap (st->dpy, xgwa.visual, xgwa.colormap,
439 st->colours, &st->ncolours,
442 /* Initialize IFS data */
444 st->delay = get_integer_resource(st->dpy, "delay", "Delay");
445 st->length = get_integer_resource(st->dpy, "length", "Detail");
446 if (st->length < 0) st->length = 0;
447 st->mode = get_integer_resource(st->dpy, "mode", "Mode");
449 st->rotate = get_boolean_resource(st->dpy, "rotate", "Boolean");
450 st->scale = get_boolean_resource(st->dpy, "scale", "Boolean");
451 st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
452 st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
453 st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
455 st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
456 if (st->lenses) free (st->lenses);
457 st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
458 if (!st->lenses) exit(1);
459 for (i = 0; i < st->lensnum; i++) {
472 ifs_reshape (Display *dpy, Window window, void *closure,
473 unsigned int w, unsigned int h)
475 struct state *st = (struct state *)closure;
476 XWindowAttributes xgwa;
478 /* oh well, we need the screen depth anyway */
479 XGetWindowAttributes (st->dpy, st->window, &xgwa);
481 st->width = xgwa.width;
482 st->widthb = ((xgwa.width + 31) >> 5);
483 st->height = xgwa.height;
484 st->width8 = xgwa.width << 8;
485 st->height8 = xgwa.height << 8;
487 if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
488 st->xmin = xgwa.width + 1;
489 st->xmax = st->ymax = -1;
490 st->ymin = xgwa.height + 1;
493 if (st->backbuffer != None && st->backbuffer != st->window) {
494 XFreePixmap(st->dpy, st->backbuffer);
495 st->backbuffer = None;
498 if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
499 st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
500 XSetForeground(st->dpy, st->gc, st->blackColor);
501 XFillRectangle(st->dpy, st->backbuffer, st->gc,
502 0, 0, st->width, st->height);
504 st->backbuffer = st->window;
505 XClearWindow(st->dpy, st->window);
508 if (st->board) free(st->board);
509 st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
510 if (!st->board) exit(1);
514 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
520 ifs_free (Display *dpy, Window window, void *closure)
522 struct state *st = (struct state *) closure;
524 if (st->board) free(st->board);
525 if (st->lenses) free(st->lenses);
526 if (st->colours) free(st->colours);
527 if (st->backbuffer != None && st->backbuffer != st->window)
528 XFreePixmap(st->dpy, st->backbuffer);
532 XSCREENSAVER_MODULE ("IFS", ifs)