/* * InterMomentary (dragorn@kismetwireless.net) * Directly ported code from complexification.net InterMomentary art * http://www.complexification.net/gallery/machines/interMomentary/applet_l/interMomentary_l.pde * * Intersecting Circles, Instantaneous * J. Tarbell + complexification.net * Albuquerque, New Mexico * May, 2004 * * a REAS collaboration for the + groupc.net * Whitney Museum of American Art ARTPORT + artport.whitney.org * Robert Hodgin + flight404.com * William Ngan + metaphorical.net * * * 1.0 Oct 10 2004 dragorn Completed first port * * * Based, of course, on other hacks in: * * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include #include "screenhack.h" #include "hsv.h" /* this program goes faster if some functions are inline. The following is * borrowed from ifs.c */ #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus) #undef inline #define inline /* */ #endif /* Pixel rider */ typedef struct { float t; float vt; float mycharge; } PxRider; /* disc of light */ typedef struct { /* index identifier */ int id; /* position */ float x, y; /* radius */ float r, dr; /* velocity */ float vx, vy; /* pixel riders */ int numr; PxRider *pxRiders; } Disc; struct field { unsigned int height; unsigned int width; int initial_discs; Disc *discs; unsigned int num; unsigned int maxrider; unsigned int maxradius; /* color parms */ unsigned long fgcolor; unsigned long bgcolor; int visdepth; unsigned int cycles; /* Offscreen image we draw to */ Pixmap off_map; unsigned char *off_alpha; }; struct state { Display *dpy; Window window; struct field *f; GC fgc, copygc; XWindowAttributes xgwa; int draw_delay; XColor *colors; int ncolors; }; static void *xrealloc(void *p, size_t size) { void *ret; if ((ret = realloc(p, size)) == NULL) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } return ret; } static struct field *init_field(void) { struct field *f = xrealloc(NULL, sizeof(struct field)); f->height = 0; f->width = 0; f->initial_discs = 0; f->discs = NULL; f->num = 0; f->maxrider = 0; f->maxradius = 0; f->cycles = 0; f->fgcolor = 0; f->bgcolor = 0; f->off_alpha = NULL; f->visdepth = 0; return f; } /* Quick-ref to pixels in the alpha map */ #define ref_pixel(f, x, y) ((f)->off_alpha[(y) * (f)->width + (x)]) static inline void make_disc(struct field *f, float x, float y, float vx, float vy, float r) { /* Synthesis of Disc::Disc and PxRider::PxRider */ Disc *nd; int ix; /* allocate a new disc */ f->discs = (Disc *) xrealloc(f->discs, sizeof(Disc) * (f->num + 1)); nd = &(f->discs[f->num]); nd->id = f->num++; nd->x = x; nd->y = y; nd->vx = vx; nd->vy = vy; nd->dr = r; nd->r = frand(r) / 3; nd->numr = (frand(r) / 2.62); if (nd->numr > f->maxrider) nd->numr = f->maxrider; nd->pxRiders = NULL; nd->pxRiders = (PxRider *) xrealloc(nd->pxRiders, sizeof(PxRider) * (f->maxrider)); for (ix = 0; ix < f->maxrider; ix++) { nd->pxRiders[ix].vt = 0.0; nd->pxRiders[ix].t = frand(M_PI * 2); nd->pxRiders[ix].mycharge = 0; } } /* alpha blended point drawing */ static inline unsigned long trans_point(struct state *st, int x1, int y1, unsigned char myc, float a, struct field *f) { if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) { if (a >= 1.0) { ref_pixel(f, x1, y1) = myc; } else { unsigned long c = ref_pixel(f, x1, y1); c = c + (myc - c) * a; ref_pixel(f, x1, y1) = c; return c; } } return 0; } static inline unsigned long get_pixel (struct state *st, unsigned char v) { return st->colors [v * (st->ncolors-1) / 255].pixel; } static inline void move_disc(struct field *f, int dnum) { Disc *d = &(f->discs[dnum]); /* add velocity to position */ d->x += d->vx; d->y += d->vy; /* bound check */ if (d->x + d->r < 0) d->x += f->width + d->r + d->r; if (d->x - d->r > f->width) d->x -= f->width + d->r + d->r; if (d->y + d->r < 0) d->y += f->height + d->r + d->r; if (d->y - d->r > f->height) d->y -= f->height + d->r + d->r; /* increase to destination radius */ if (d->r < d->dr) d->r += 0.1; } static inline void draw_glowpoint(struct state *st, Drawable drawable, GC fgc, struct field *f, float px, float py) { int i, j; float a; unsigned long c; for (i = -2; i < 3; i++) { for (j = -2; j < 3; j++) { a = 0.8 - i * i * 0.1 - j * j * 0.1; c = trans_point(st, px+i, py+j, 255, a, f); XSetForeground(st->dpy, fgc, get_pixel (st, c)); XDrawPoint(st->dpy, drawable, fgc, px + i, py + j); XSetForeground(st->dpy, fgc, f->fgcolor); } } } static inline void moverender_rider(struct state *st, Drawable drawable, GC fgc, struct field *f, PxRider *rid, float x, float y, float r) { float px, py; unsigned long int c; double cv; /* add velocity to theta */ rid->t = fmod((rid->t + rid->vt + M_PI), (2 * M_PI)) - M_PI; rid->vt += frand(0.002) - 0.001; /* apply friction brakes */ if (fabsf(rid->vt) > 0.02) rid->vt *= 0.9; /* draw */ px = x + r * cos(rid->t); py = y + r * sin(rid->t); if ((px < 0) || (px >= f->width) || (py < 0) || (py >= f->height)) return; /* max brightness seems to be 0.003845 */ c = ref_pixel(f, (int) px, (int) py); cv = c / 255.0; /* guestimated - 40 is 18% of 255, so scale this to 0.0 to 0.003845 */ if (cv > 0.0006921) { draw_glowpoint(st, drawable, fgc, f, px, py); rid->mycharge = 0.003845; } else { rid->mycharge *= 0.98; c = 255 * rid->mycharge; trans_point(st, px, py, c, 0.5, f); XSetForeground(st->dpy, fgc, get_pixel(st, c)); XDrawPoint(st->dpy, drawable, fgc, px, py); XSetForeground(st->dpy, fgc, f->fgcolor); } } static inline void render_disc(struct state *st, Drawable drawable, GC fgc, struct field *f, int dnum) { Disc *di = &(f->discs[dnum]); int n, m; float dx, dy, d; float a, p2x, p2y, h, p3ax, p3ay, p3bx, p3by; unsigned long c; /* Find intersecting points with all ascending discs */ for (n = di->id + 1; n < f->num; n++) { dx = f->discs[n].x - di->x; dy = f->discs[n].y - di->y; d = sqrt(dx * dx + dy * dy); /* intersection test */ if (d < (f->discs[n].r + di->r)) { /* complete containment test */ if (d > fabsf(f->discs[n].r - di->r)) { /* find solutions */ a = (di->r * di->r - f->discs[n].r * f->discs[n].r + d * d) / (2 * d); p2x = di->x + a * (f->discs[n].x - di->x) / d; p2y = di->y + a * (f->discs[n].y - di->y) / d; h = sqrt(di->r * di->r - a * a); p3ax = p2x + h * (f->discs[n].y - di->y) / d; p3ay = p2y - h * (f->discs[n].x - di->x) / d; p3bx = p2x - h * (f->discs[n].y - di->y) / d; p3by = p2y + h * (f->discs[n].x - di->x) / d; /* bounds check */ if ((p3ax < 0) || (p3ax >= f->width) || (p3ay < 0) || (p3ay >= f->height) || (p3bx < 0) || (p3bx >= f->width) || (p3by < 0) || (p3by >= f->height)) continue; /* p3a and p3b might be identical, ignore this case for now */ /* XPutPixel(f->off_map, p3ax, p3ay, f->fgcolor); */ c = trans_point(st, p3ax, p3ay, 255, 0.75, f); XSetForeground(st->dpy, fgc, get_pixel (st, c)); XDrawPoint(st->dpy, drawable, fgc, p3ax, p3ay); /* XPutPixel(f->off_map, p3bx, p3by, f->fgcolor); */ c = trans_point(st, p3bx, p3by, 255, 0.75, f); XSetForeground(st->dpy, fgc, get_pixel (st, c)); XDrawPoint(st->dpy, drawable, fgc, p3bx, p3by); XSetForeground(st->dpy, fgc, f->fgcolor); } } } /* Render all the pixel riders */ for (m = 0; m < di->numr; m++) { moverender_rider(st, drawable, fgc, f, &(di->pxRiders[m]), di->x, di->y, di->r); } } static void build_img(Display *dpy, Window window, struct field *f) { if (f->off_alpha) { free(f->off_alpha); f->off_alpha = NULL; /* Assume theres also an off pixmap */ if (f->off_map != window) XFreePixmap(dpy, f->off_map); } f->off_alpha = (unsigned char *) xrealloc(f->off_alpha, sizeof(unsigned char) * f->width * f->height); memset(f->off_alpha, 0, sizeof(unsigned char) * f->width * f->height); # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ /* Or Android's */ f->off_map = window; # else f->off_map = XCreatePixmap(dpy, window, f->width, f->height, f->visdepth); # endif } static inline void blank_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, struct field *f) { memset(f->off_alpha, 0, sizeof(unsigned char) * f->width * f->height); XSetForeground(dpy, fgc, f->bgcolor); XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height); XSetForeground(dpy, fgc, f->fgcolor); } static void * intermomentary_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); #ifdef TIME_ME time_t start_time = time(NULL); #endif int tempx; XGCValues gcv; st->dpy = dpy; st->window = window; XGetWindowAttributes(dpy, window, &st->xgwa); st->ncolors = get_integer_resource (st->dpy, "colors", "Colors"); st->ncolors++; st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1)); gcv.foreground = get_pixel_resource(dpy, st->xgwa.colormap, "foreground", "Foreground"); gcv.background = get_pixel_resource(dpy, st->xgwa.colormap, "background", "Background"); { XColor fgc, bgc; int fgh, bgh; double fgs, fgv, bgs, bgv; fgc.pixel = gcv.foreground; bgc.pixel = gcv.background; XQueryColor (st->dpy, st->xgwa.colormap, &fgc); XQueryColor (st->dpy, st->xgwa.colormap, &bgc); rgb_to_hsv (fgc.red, fgc.green, fgc.blue, &fgh, &fgs, &fgv); rgb_to_hsv (bgc.red, bgc.green, bgc.blue, &bgh, &bgs, &bgv); #if 0 bgh = fgh; bgs = fgs; bgv = fgv / 10.0; #endif make_color_ramp (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap, bgh, bgs, bgv, fgh, fgs, fgv, st->colors, &st->ncolors, False, /* closed */ True, False); } st->f = init_field(); st->f->height = st->xgwa.height; st->f->width = st->xgwa.width; st->f->visdepth = st->xgwa.depth; st->draw_delay = (get_integer_resource(dpy, "drawDelay", "Integer")); st->f->maxrider = (get_integer_resource(dpy, "maxRiders", "Integer")); st->f->maxradius = (get_integer_resource(dpy, "maxRadius", "Integer")); st->f->initial_discs = (get_integer_resource(dpy, "numDiscs", "Integer")); if (st->f->initial_discs <= 10) { fprintf(stderr, "%s: Initial discs must be greater than 10\n", progname); exit (1); } if (st->f->maxradius <= 30) { fprintf(stderr, "%s: Max radius must be greater than 30\n", progname); exit (1); } if (st->f->maxrider <= 10) { fprintf(stderr, "%s: Max riders must be greater than 10\n", progname); exit (1); } st->fgc = XCreateGC(dpy, window, GCForeground, &gcv); st->copygc = XCreateGC(dpy, window, GCForeground, &gcv); st->f->fgcolor = gcv.foreground; st->f->bgcolor = gcv.background; /* Initialize stuff */ build_img(dpy, window, st->f); for (tempx = 0; tempx < st->f->initial_discs; tempx++) { float fx, fy, x, y, r; int bt; /* Arrange in anti-collapsing circle */ fx = 0.4 * st->f->width * cos((2 * M_PI) * tempx / st->f->initial_discs); fy = 0.4 * st->f->height * sin((2 * M_PI) * tempx / st->f->initial_discs); x = frand(st->f->width / 2) + fx; y = frand(st->f->height / 2) + fy; r = 5 + frand(st->f->maxradius); bt = 1; if ((random() % 100) < 50) bt = -1; make_disc(st->f, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r); } return st; } static unsigned long intermomentary_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int tempx; if ((st->f->cycles % 10) == 0) { /* Restart if the window size changes */ XGetWindowAttributes(dpy, window, &st->xgwa); if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) { st->f->height = st->xgwa.height; st->f->width = st->xgwa.width; st->f->visdepth = st->xgwa.depth; build_img(dpy, window, st->f); } } blank_img(dpy, st->f->off_map, st->xgwa, st->fgc, st->f); for (tempx = 0; tempx < st->f->num; tempx++) { move_disc(st->f, tempx); render_disc(st, st->f->off_map, st->fgc, st->f, tempx); } #if 0 XSetFillStyle(dpy, st->copygc, FillTiled); XSetTile(dpy, st->copygc, st->f->off_map); XFillRectangle(dpy, window, st->copygc, 0, 0, st->f->width, st->f->height); #else if (st->f->off_map != window) XCopyArea (dpy, st->f->off_map, window, st->copygc, 0, 0, st->f->width, st->f->height, 0, 0); #endif st->f->cycles++; return st->draw_delay; } static void intermomentary_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { } static Bool intermomentary_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void intermomentary_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; if (st->f) { if (st->f->off_alpha) free(st->f->off_alpha); if (st->f->off_map != window) XFreePixmap (dpy, st->f->off_map); if (st->f->discs) { int i; for (i = 0; i < st->f->num; i++) { if (st->f->discs[i].pxRiders) free (st->f->discs[i].pxRiders); } free (st->f->discs); } free (st->f); } if (st->colors) free (st->colors); XFreeGC (dpy, st->fgc); XFreeGC (dpy, st->copygc); free (st); } static const char *intermomentary_defaults[] = { ".lowrez: true", ".background: black", ".foreground: yellow", "*drawDelay: 30000", "*numDiscs: 85", "*maxRiders: 40", "*maxRadius: 100", "*colors: 256", # ifdef HAVE_MOBILE "*ignoreRotation: True", # endif 0 }; static XrmOptionDescRec intermomentary_options[] = { {"-background", ".background", XrmoptionSepArg, 0}, {"-foreground", ".foreground", XrmoptionSepArg, 0}, {"-draw-delay", ".drawDelay", XrmoptionSepArg, 0}, {"-num-discs", ".numDiscs", XrmoptionSepArg, 0}, {"-max-riders", ".maxRiders", XrmoptionSepArg, 0}, {"-max-radius", ".maxRadius", XrmoptionSepArg, 0}, { "-colors", ".colors", XrmoptionSepArg, 0 }, {0, 0, 0, 0} }; XSCREENSAVER_MODULE ("Intermomentary", intermomentary)