2 * InterMomentary (dragorn@kismetwireless.net)
3 * Directly ported code from complexification.net InterMomentary art
4 * http://www.complexification.net/gallery/machines/interMomentary/applet_l/interMomentary_l.pde
6 * Intersecting Circles, Instantaneous
7 * J. Tarbell + complexification.net
8 * Albuquerque, New Mexico
11 * a REAS collaboration for the + groupc.net
12 * Whitney Museum of American Art ARTPORT + artport.whitney.org
13 * Robert Hodgin + flight404.com
14 * William Ngan + metaphorical.net
17 * 1.0 Oct 10 2004 dragorn Completed first port
20 * Based, of course, on other hacks in:
22 * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
24 * Permission to use, copy, modify, distribute, and sell this software and its
25 * documentation for any purpose is hereby granted without fee, provided that
26 * the above copyright notice appear in all copies and that both that
27 * copyright notice and this permission notice appear in supporting
28 * documentation. No representations are made about the suitability of this
29 * software for any purpose. It is provided "as is" without express or
33 #include "screenhack.h"
34 #include <X11/Xutil.h>
40 #define MAX_WIDTH SHRT_MAX
51 /* this program goes faster if some functions are inline. The following is
52 * borrowed from ifs.c */
53 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
67 /* index identifier */
90 unsigned int maxrider;
91 unsigned int maxradius;
94 unsigned long fgcolor;
95 unsigned long bgcolor;
100 /* Offscreen image we draw to */
102 unsigned long int *off_alpha;
105 static void *xrealloc(void *p, size_t size) {
107 if ((ret = realloc(p, size)) == NULL) {
108 fprintf(stderr, "%s: out of memory\n", progname);
114 struct field *init_field(void) {
115 struct field *f = xrealloc(NULL, sizeof(struct field));
118 f->initial_discs = 0;
131 /* Quick-ref to pixels in the alpha map */
132 #define ref_pixel(f, x, y) ((f)->off_alpha[(y) * (f)->width + (x)])
134 inline void make_disc(struct field *f, float x, float y, float vx, float vy, float r) {
135 /* Synthesis of Disc::Disc and PxRider::PxRider */
139 /* allocate a new disc */
140 f->discs = (Disc *) xrealloc(f->discs, sizeof(Disc) * (f->num + 1));
142 nd = &(f->discs[f->num]);
150 nd->r = frand(r) / 3;
152 nd->numr = (frand(r) / 2.62);
153 if (nd->numr > f->maxrider)
154 nd->numr = f->maxrider;
157 nd->pxRiders = (PxRider *) xrealloc(nd->pxRiders, sizeof(PxRider) * (f->maxrider));
158 for (ix = 0; ix < f->maxrider; ix++) {
159 nd->pxRiders[ix].vt = 0.0;
160 nd->pxRiders[ix].t = frand(M_PI * 2);
161 nd->pxRiders[ix].mycharge = 0;
165 inline void point2rgb(int depth, unsigned long c, unsigned short int *r,
166 unsigned short int *g, unsigned short int *b) {
170 *g = (c & 0xff00) >> 8;
171 *r = (c & 0xff0000) >> 16;
175 *g = ((c >> 5) & 0x3f) << 2;
176 *r = ((c >> 11) & 0x1f) << 3;
177 *b = (c & 0x1f) << 3;
180 *g = ((c >> 5) & 0x1f) << 3;
181 *r = ((c >> 10) & 0x1f) << 3;
182 *b = (c & 0x1f) << 3;
187 inline unsigned long rgb2point(int depth, unsigned short int r,
188 unsigned short int g, unsigned short int b) {
189 unsigned long ret = 0;
195 ret |= (r << 16) | (g << 8) | b;
198 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
201 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
208 /* alpha blended point drawing */
209 inline unsigned long trans_point(int x1, int y1, unsigned long myc, float a, struct field *f) {
210 if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
212 ref_pixel(f, x1, y1) = myc;
214 unsigned short int or, og, ob;
215 unsigned short int r, g, b;
216 unsigned short int nr, ng, nb;
219 c = ref_pixel(f, x1, y1);
220 point2rgb(f->visdepth, c, &or, &og, &ob);
221 point2rgb(f->visdepth, myc, &r, &g, &b);
223 nr = or + (r - or) * a;
224 ng = og + (g - og) * a;
225 nb = ob + (b - ob) * a;
227 c = rgb2point(f->visdepth, nr, ng, nb);
228 ref_pixel(f, x1, y1) = c;
237 inline void move_disc(struct field *f, int dnum) {
238 Disc *d = &(f->discs[dnum]);
240 /* add velocity to position */
246 d->x += f->width + d->r + d->r;
247 if (d->x - d->r > f->width)
248 d->x -= f->width + d->r + d->r;
250 d->y += f->height + d->r + d->r;
251 if (d->y - d->r > f->height)
252 d->y -= f->height + d->r + d->r;
254 /* increase to destination radius */
259 inline void draw_glowpoint(Display *dpy, Window window, GC fgc, struct field *f, float px, float py) {
264 for (i =- 2; i < 3; i++) {
265 for (j =- 2; j < 3; j++) {
266 a = 0.8 - i * i * 0.1 - j * j * 0.1;
268 c = trans_point(px+i, py+j, f->fgcolor, a, f);
269 XSetForeground(dpy, fgc, c);
270 XDrawPoint(dpy, window, fgc, px + i, py + j);
271 XSetForeground(dpy, fgc, f->fgcolor);
276 inline void moverender_rider(Display *dpy, Window window, GC fgc, struct field *f, PxRider *rid,
277 float x, float y, float r) {
280 unsigned short int cr, cg, cb;
284 /* add velocity to theta */
285 rid->t = fmodf((rid->t + rid->vt + M_PI), (2 * M_PI)) - M_PI;
287 rid->vt += frand(0.002) - 0.001;
289 /* apply friction brakes */
290 if (abs(rid->vt) > 0.02)
294 px = x + r * cos(rid->t);
295 py = y + r * sin(rid->t);
297 if ((px < 0) || (px >= f->width) || (py < 0) || (py >= f->height))
300 /* max brightness seems to be 0.003845 */
302 c = ref_pixel(f, (int) px, (int) py);
303 point2rgb(f->visdepth, c, &cr, &cg, &cb);
304 rgb_to_hsv(cr, cg, cb, &ch, &cs, &cv);
306 /* guestimated - 40 is 18% of 255, so scale this to 0.0 to 0.003845 */
307 if (cv > 0.0006921) {
308 draw_glowpoint(dpy, window, fgc, f, px, py);
310 rid->mycharge = 0.003845;
312 rid->mycharge *= 0.98;
314 hsv_to_rgb(ch, cs, rid->mycharge, &cr, &cg, &cb);
315 c = rgb2point(f->visdepth, cr, cg, cb);
317 trans_point(px, py, c, 0.5, f);
319 XSetForeground(dpy, fgc, c);
320 XDrawPoint(dpy, window, fgc, px, py);
321 XSetForeground(dpy, fgc, f->fgcolor);
325 inline void render_disc(Display *dpy, Window window, GC fgc, struct field *f, int dnum) {
326 Disc *di = &(f->discs[dnum]);
329 float a, p2x, p2y, h, p3ax, p3ay, p3bx, p3by;
332 /* Find intersecting points with all ascending discs */
333 for (n = di->id + 1; n < f->num; n++) {
334 dx = f->discs[n].x - di->x;
335 dy = f->discs[n].y - di->y;
336 d = sqrt(dx * dx + dy * dy);
338 /* intersection test */
339 if (d < (f->discs[n].r + di->r)) {
340 /* complete containment test */
341 if (d > abs(f->discs[n].r - di->r)) {
343 a = (di->r * di->r - f->discs[n].r * f->discs[n].r + d * d) / (2 * d);
344 p2x = di->x + a * (f->discs[n].x - di->x) / d;
345 p2y = di->y + a * (f->discs[n].y - di->y) / d;
347 h = sqrt(di->r * di->r - a * a);
349 p3ax = p2x + h * (f->discs[n].y - di->y) / d;
350 p3ay = p2y - h * (f->discs[n].x - di->x) / d;
352 p3bx = p2x - h * (f->discs[n].y - di->y) / d;
353 p3by = p2y + h * (f->discs[n].x - di->x) / d;
356 if ((p3ax < 0) || (p3ax >= f->width) || (p3ay < 0) || (p3ay >= f->height) ||
357 (p3bx < 0) || (p3bx >= f->width) || (p3by < 0) || (p3by >= f->height))
360 /* p3a and p3b might be identical, ignore this case for now */
361 /* XPutPixel(f->off_map, p3ax, p3ay, f->fgcolor); */
362 c = trans_point(p3ax, p3ay, f->fgcolor, 0.75, f);
363 XSetForeground(dpy, fgc, c);
364 XDrawPoint(dpy, window, fgc, p3ax, p3ay);
366 /* XPutPixel(f->off_map, p3bx, p3by, f->fgcolor); */
367 c = trans_point(p3bx, p3by, f->fgcolor, 0.75, f);
368 XSetForeground(dpy, fgc, c);
369 XDrawPoint(dpy, window, fgc, p3bx, p3by);
370 XSetForeground(dpy, fgc, f->fgcolor);
376 /* Render all the pixel riders */
377 for (m = 0; m < di->numr; m++) {
378 moverender_rider(dpy, window, fgc, f, &(di->pxRiders[m]),
379 di->x, di->y, di->r);
383 char *progclass = "InterMomentary";
386 ".background: black",
387 ".foreground: white",
395 XrmOptionDescRec options[] = {
396 {"-background", ".background", XrmoptionSepArg, 0},
397 {"-foreground", ".foreground", XrmoptionSepArg, 0},
398 {"-draw-delay", ".drawDelay", XrmoptionSepArg, 0},
399 {"-num-discs", ".numDiscs", XrmoptionSepArg, 0},
400 {"-max-riders", ".maxRiders", XrmoptionSepArg, 0},
401 {"-max-radius", ".maxRadius", XrmoptionSepArg, 0},
405 void build_img(Display *dpy, Window window, struct field *f) {
410 /* Assume theres also an off pixmap */
411 XFreePixmap(dpy, f->off_map);
414 f->off_alpha = (unsigned long *) xrealloc(f->off_alpha, sizeof(unsigned long) *
415 f->width * f->height);
417 memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
419 f->off_map = XCreatePixmap(dpy, window, f->width, f->height, f->visdepth);
423 inline void blank_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, struct field *f) {
424 memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
426 XSetForeground(dpy, fgc, f->bgcolor);
427 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
428 XSetForeground(dpy, fgc, f->fgcolor);
431 void screenhack(Display * dpy, Window window)
433 struct field *f = init_field();
436 time_t start_time = time(NULL);
444 XWindowAttributes xgwa;
446 draw_delay = (get_integer_resource("drawDelay", "Integer"));
447 f->maxrider = (get_integer_resource("maxRiders", "Integer"));
448 f->maxradius = (get_integer_resource("maxRadius", "Integer"));
449 f->initial_discs = (get_integer_resource("numDiscs", "Integer"));
451 if (f->initial_discs <= 10) {
452 fprintf(stderr, "%s: Initial discs must be greater than 10\n", progname);
456 if (f->maxradius <= 30) {
457 fprintf(stderr, "%s: Max radius must be greater than 30\n", progname);
461 if (f->maxrider <= 10) {
462 fprintf(stderr, "%s: Max riders must be greater than 10\n", progname);
466 XGetWindowAttributes(dpy, window, &xgwa);
468 f->height = xgwa.height;
469 f->width = xgwa.width;
470 f->visdepth = xgwa.depth;
472 gcv.foreground = get_pixel_resource("foreground", "Foreground",
474 gcv.background = get_pixel_resource("background", "Background",
476 fgc = XCreateGC(dpy, window, GCForeground, &gcv);
477 copygc = XCreateGC(dpy, window, GCForeground, &gcv);
479 f->fgcolor = gcv.foreground;
480 f->bgcolor = gcv.background;
482 /* Initialize stuff */
483 build_img(dpy, window, f);
485 for (tempx = 0; tempx < f->initial_discs; tempx++) {
486 float fx, fy, x, y, r;
489 /* Arrange in anti-collapsing circle */
490 fx = 0.4 * f->width * cos((2 * M_PI) * tempx / f->initial_discs);
491 fy = 0.4 * f->height * sin((2 * M_PI) * tempx / f->initial_discs);
492 x = frand(f->width / 2) + fx;
493 y = frand(f->height / 2) + fy;
494 r = 5 + frand(f->maxradius);
497 if ((random() % 100) < 50)
500 make_disc(f, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r);
505 if ((f->cycles % 10) == 0) {
506 /* Restart if the window size changes */
507 XGetWindowAttributes(dpy, window, &xgwa);
509 if (f->height != xgwa.height || f->width != xgwa.width) {
510 f->height = xgwa.height;
511 f->width = xgwa.width;
512 f->visdepth = xgwa.depth;
514 build_img(dpy, window, f);
518 blank_img(dpy, f->off_map, xgwa, fgc, f);
519 for (tempx = 0; tempx < f->num; tempx++) {
521 render_disc(dpy, f->off_map, fgc, f, tempx);
524 XSetFillStyle(dpy, copygc, FillTiled);
525 XSetTile(dpy, copygc, f->off_map);
526 XFillRectangle(dpy, window, copygc, 0, 0, f->width, f->height);
530 /* XSync(dpy, False); */
532 screenhack_handle_events(dpy);