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
37 #include "screenhack.h"
40 #define countof(x) (sizeof((x)) / sizeof(*(x)))
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 */
48 int ua, ub, utx; /* Precomputed combined r,s,t values */
49 int uc, ud, uty; /* Precomputed combined r,s,t values */
61 int blackColor, whiteColor;
63 int width, widthb, height;
66 XPoint pointbuf[1000];
68 int xmin, xmax, ymin, ymax;
78 Bool translate, scale, rotate;
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)))
87 return (((float)random() / RAND_MAX) * up);
90 static const char *ifs_defaults [] = {
101 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
102 "*doubleBuffer: False",
104 "*doubleBuffer: True",
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" },
126 /* Draw all the queued points on the backbuffer */
128 drawpoints(struct state *st)
130 XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
135 /* Set a point to be drawn, if it hasn't been already.
136 * Expects coordinates in 256ths of a pixel. */
138 sp(struct state *st, int x, int y)
140 if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
146 if (getdot(x, y)) return;
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;
154 st->pointbuf[st->npoints].x = x;
155 st->pointbuf[st->npoints].y = y;
158 if (st->npoints >= countof(st->pointbuf)) {
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|
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.
174 lensmatrix(struct state *st, Lens *l)
176 l->ua = 1024.0 * l->s * cos(l->r);
177 l->ub = -1024.0 * l->s * sin(l->r);
180 l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
182 l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
187 CreateLens(struct state *st,
194 newlens->sa = newlens->txa = newlens->tya = 0;
196 newlens->r = newlens->ro = newlens->rt = nr;
202 newlens->s = newlens->so = newlens->st = ns;
205 else newlens->s = 0.5;
210 lensmatrix(st, newlens);
214 mutate(struct state *st, Lens *l)
221 l->rt = myrandom(4) - 2;
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;
230 /* Reset counter, obtain new target value */
233 l->st = myrandom(2) - 1;
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;
243 l->txa += myrandom(0.004) - 0.002;
244 l->tya += myrandom(0.004) - 0.002;
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;
254 if (st->rotate || st->scale || st->translate) {
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)*/
264 /* Calls itself <lensnum> times - with results from each lens/function. *
265 * After <length> calls to itself, it stops iterating and draws a point. */
267 recurse(struct state *st, int x, int y, int length)
275 for (i = 0; i < st->lensnum; i++) {
277 recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1);
282 /* Performs <count> random lens transformations, drawing a point at each
283 * iteration after the first 10.
286 iterate(struct state *st, int count)
295 l = &st->lenses[random() % st->lensnum]; \
296 tx = STEPX(l, x, y); \
297 y = STEPY(l, x, y); \
300 for (i = 0; i < 10; i++) {
304 for ( ; i < count; i++) {
315 /* Come on and iterate, iterate, iterate and sing... *
316 * Yeah, this function just calls iterate, mutate, *
317 * and then draws everything. */
319 ifs_draw (Display *dpy, Window window, void *closure)
321 struct state *st = (struct state *) closure;
323 int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
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,
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;
337 st->ccolour %= st->ncolours;
338 XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
340 /* calculate and draw points for this frame */
341 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
343 recurse(st, st->width << 7, st->height << 7, st->length);
345 iterate(st, pow(st->lensnum, st->length));
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,
360 xmax - xmin + 1, ymax - ymin + 1,
364 for(i = 0; i < st->lensnum; i++) {
365 mutate(st, &st->lenses[i]);
372 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
375 ifs_init (Display *d_arg, Window w_arg)
377 struct state *st = (struct state *) calloc (1, sizeof(*st));
379 XWindowAttributes xgwa;
381 /* Initialise all this X shizzle */
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);
389 XGetWindowAttributes (st->dpy, st->window, &xgwa);
390 ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
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,
402 /* Initialize IFS data */
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");
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");
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++) {
431 ifs_reshape (Display *dpy, Window window, void *closure,
432 unsigned int w, unsigned int h)
434 struct state *st = (struct state *)closure;
435 XWindowAttributes xgwa;
437 /* oh well, we need the screen depth anyway */
438 XGetWindowAttributes (st->dpy, st->window, &xgwa);
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;
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;
452 if (st->backbuffer != None && st->backbuffer != st->window) {
453 XFreePixmap(st->dpy, st->backbuffer);
454 st->backbuffer = None;
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);
463 st->backbuffer = st->window;
464 XClearWindow(st->dpy, st->window);
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);
473 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
479 ifs_free (Display *dpy, Window window, void *closure)
481 struct state *st = (struct state *) closure;
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);
491 XSCREENSAVER_MODULE ("IFS", ifs)