summaryrefslogtreecommitdiffstats
path: root/hacks/intermomentary.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/intermomentary.c')
-rw-r--r--hacks/intermomentary.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/hacks/intermomentary.c b/hacks/intermomentary.c
new file mode 100644
index 0000000..4e65b76
--- /dev/null
+++ b/hacks/intermomentary.c
@@ -0,0 +1,577 @@
+/*
+ * 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 <jwz@jwz.org>
+ *
+ * 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 <math.h>
+#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;
+ 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)