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
34 #include "screenhack.h"
35 #include <X11/Xutil.h>
41 #define MAX_WIDTH SHRT_MAX
52 /* this program goes faster if some functions are inline. The following is
53 * borrowed from ifs.c */
54 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
68 /* index identifier */
91 unsigned int maxrider;
92 unsigned int maxradius;
95 unsigned long fgcolor;
96 unsigned long bgcolor;
101 /* Offscreen image we draw to */
103 unsigned long int *off_alpha;
106 static void *xrealloc(void *p, size_t size) {
108 if ((ret = realloc(p, size)) == NULL) {
109 fprintf(stderr, "%s: out of memory\n", progname);
115 struct field *init_field(void) {
116 struct field *f = xrealloc(NULL, sizeof(struct field));
119 f->initial_discs = 0;
132 /* Quick-ref to pixels in the alpha map */
133 #define ref_pixel(f, x, y) ((f)->off_alpha[(y) * (f)->width + (x)])
135 inline void make_disc(struct field *f, float x, float y, float vx, float vy, float r) {
136 /* Synthesis of Disc::Disc and PxRider::PxRider */
140 /* allocate a new disc */
141 f->discs = (Disc *) xrealloc(f->discs, sizeof(Disc) * (f->num + 1));
143 nd = &(f->discs[f->num]);
151 nd->r = frand(r) / 3;
153 nd->numr = (frand(r) / 2.62);
154 if (nd->numr > f->maxrider)
155 nd->numr = f->maxrider;
158 nd->pxRiders = (PxRider *) xrealloc(nd->pxRiders, sizeof(PxRider) * (f->maxrider));
159 for (ix = 0; ix < f->maxrider; ix++) {
160 nd->pxRiders[ix].vt = 0.0;
161 nd->pxRiders[ix].t = frand(M_PI * 2);
162 nd->pxRiders[ix].mycharge = 0;
166 inline void point2rgb(int depth, unsigned long c, unsigned short int *r,
167 unsigned short int *g, unsigned short int *b) {
171 *g = (c & 0xff00) >> 8;
172 *r = (c & 0xff0000) >> 16;
176 *g = ((c >> 5) & 0x3f) << 2;
177 *r = ((c >> 11) & 0x1f) << 3;
178 *b = (c & 0x1f) << 3;
181 *g = ((c >> 5) & 0x1f) << 3;
182 *r = ((c >> 10) & 0x1f) << 3;
183 *b = (c & 0x1f) << 3;
188 inline unsigned long rgb2point(int depth, unsigned short int r,
189 unsigned short int g, unsigned short int b) {
190 unsigned long ret = 0;
196 ret |= (r << 16) | (g << 8) | b;
199 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
202 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
209 /* alpha blended point drawing */
210 inline unsigned long trans_point(int x1, int y1, unsigned long myc, float a, struct field *f) {
211 if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
213 ref_pixel(f, x1, y1) = myc;
215 unsigned short int or = 0, og = 0, ob = 0;
216 unsigned short int r = 0, g = 0, b = 0;
217 unsigned short int nr, ng, nb;
220 c = ref_pixel(f, x1, y1);
221 point2rgb(f->visdepth, c, &or, &og, &ob);
222 point2rgb(f->visdepth, myc, &r, &g, &b);
224 nr = or + (r - or) * a;
225 ng = og + (g - og) * a;
226 nb = ob + (b - ob) * a;
228 c = rgb2point(f->visdepth, nr, ng, nb);
229 ref_pixel(f, x1, y1) = c;
238 inline void move_disc(struct field *f, int dnum) {
239 Disc *d = &(f->discs[dnum]);
241 /* add velocity to position */
247 d->x += f->width + d->r + d->r;
248 if (d->x - d->r > f->width)
249 d->x -= f->width + d->r + d->r;
251 d->y += f->height + d->r + d->r;
252 if (d->y - d->r > f->height)
253 d->y -= f->height + d->r + d->r;
255 /* increase to destination radius */
260 inline void draw_glowpoint(Display *dpy, Window window, GC fgc, struct field *f, float px, float py) {
265 for (i =- 2; i < 3; i++) {
266 for (j =- 2; j < 3; j++) {
267 a = 0.8 - i * i * 0.1 - j * j * 0.1;
269 c = trans_point(px+i, py+j, f->fgcolor, a, f);
270 XSetForeground(dpy, fgc, c);
271 XDrawPoint(dpy, window, fgc, px + i, py + j);
272 XSetForeground(dpy, fgc, f->fgcolor);
277 inline void moverender_rider(Display *dpy, Window window, GC fgc, struct field *f, PxRider *rid,
278 float x, float y, float r) {
281 unsigned short int cr, cg, cb;
285 /* add velocity to theta */
286 rid->t = fmod((rid->t + rid->vt + M_PI), (2 * M_PI)) - M_PI;
288 rid->vt += frand(0.002) - 0.001;
290 /* apply friction brakes */
291 if (abs(rid->vt) > 0.02)
295 px = x + r * cos(rid->t);
296 py = y + r * sin(rid->t);
298 if ((px < 0) || (px >= f->width) || (py < 0) || (py >= f->height))
301 /* max brightness seems to be 0.003845 */
303 c = ref_pixel(f, (int) px, (int) py);
304 point2rgb(f->visdepth, c, &cr, &cg, &cb);
305 rgb_to_hsv(cr, cg, cb, &ch, &cs, &cv);
307 /* guestimated - 40 is 18% of 255, so scale this to 0.0 to 0.003845 */
308 if (cv > 0.0006921) {
309 draw_glowpoint(dpy, window, fgc, f, px, py);
311 rid->mycharge = 0.003845;
313 rid->mycharge *= 0.98;
315 hsv_to_rgb(ch, cs, rid->mycharge, &cr, &cg, &cb);
316 c = rgb2point(f->visdepth, cr, cg, cb);
318 trans_point(px, py, c, 0.5, f);
320 XSetForeground(dpy, fgc, c);
321 XDrawPoint(dpy, window, fgc, px, py);
322 XSetForeground(dpy, fgc, f->fgcolor);
326 inline void render_disc(Display *dpy, Window window, GC fgc, struct field *f, int dnum) {
327 Disc *di = &(f->discs[dnum]);
330 float a, p2x, p2y, h, p3ax, p3ay, p3bx, p3by;
333 /* Find intersecting points with all ascending discs */
334 for (n = di->id + 1; n < f->num; n++) {
335 dx = f->discs[n].x - di->x;
336 dy = f->discs[n].y - di->y;
337 d = sqrt(dx * dx + dy * dy);
339 /* intersection test */
340 if (d < (f->discs[n].r + di->r)) {
341 /* complete containment test */
342 if (d > abs(f->discs[n].r - di->r)) {
344 a = (di->r * di->r - f->discs[n].r * f->discs[n].r + d * d) / (2 * d);
345 p2x = di->x + a * (f->discs[n].x - di->x) / d;
346 p2y = di->y + a * (f->discs[n].y - di->y) / d;
348 h = sqrt(di->r * di->r - a * a);
350 p3ax = p2x + h * (f->discs[n].y - di->y) / d;
351 p3ay = p2y - h * (f->discs[n].x - di->x) / d;
353 p3bx = p2x - h * (f->discs[n].y - di->y) / d;
354 p3by = p2y + h * (f->discs[n].x - di->x) / d;
357 if ((p3ax < 0) || (p3ax >= f->width) || (p3ay < 0) || (p3ay >= f->height) ||
358 (p3bx < 0) || (p3bx >= f->width) || (p3by < 0) || (p3by >= f->height))
361 /* p3a and p3b might be identical, ignore this case for now */
362 /* XPutPixel(f->off_map, p3ax, p3ay, f->fgcolor); */
363 c = trans_point(p3ax, p3ay, f->fgcolor, 0.75, f);
364 XSetForeground(dpy, fgc, c);
365 XDrawPoint(dpy, window, fgc, p3ax, p3ay);
367 /* XPutPixel(f->off_map, p3bx, p3by, f->fgcolor); */
368 c = trans_point(p3bx, p3by, f->fgcolor, 0.75, f);
369 XSetForeground(dpy, fgc, c);
370 XDrawPoint(dpy, window, fgc, p3bx, p3by);
371 XSetForeground(dpy, fgc, f->fgcolor);
377 /* Render all the pixel riders */
378 for (m = 0; m < di->numr; m++) {
379 moverender_rider(dpy, window, fgc, f, &(di->pxRiders[m]),
380 di->x, di->y, di->r);
384 char *progclass = "InterMomentary";
387 ".background: black",
388 ".foreground: white",
396 XrmOptionDescRec options[] = {
397 {"-background", ".background", XrmoptionSepArg, 0},
398 {"-foreground", ".foreground", XrmoptionSepArg, 0},
399 {"-draw-delay", ".drawDelay", XrmoptionSepArg, 0},
400 {"-num-discs", ".numDiscs", XrmoptionSepArg, 0},
401 {"-max-riders", ".maxRiders", XrmoptionSepArg, 0},
402 {"-max-radius", ".maxRadius", XrmoptionSepArg, 0},
406 void build_img(Display *dpy, Window window, struct field *f) {
411 /* Assume theres also an off pixmap */
412 XFreePixmap(dpy, f->off_map);
415 f->off_alpha = (unsigned long *) xrealloc(f->off_alpha, sizeof(unsigned long) *
416 f->width * f->height);
418 memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
420 f->off_map = XCreatePixmap(dpy, window, f->width, f->height, f->visdepth);
424 inline void blank_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, struct field *f) {
425 memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
427 XSetForeground(dpy, fgc, f->bgcolor);
428 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
429 XSetForeground(dpy, fgc, f->fgcolor);
432 void screenhack(Display * dpy, Window window)
434 struct field *f = init_field();
437 time_t start_time = time(NULL);
445 XWindowAttributes xgwa;
447 draw_delay = (get_integer_resource("drawDelay", "Integer"));
448 f->maxrider = (get_integer_resource("maxRiders", "Integer"));
449 f->maxradius = (get_integer_resource("maxRadius", "Integer"));
450 f->initial_discs = (get_integer_resource("numDiscs", "Integer"));
452 if (f->initial_discs <= 10) {
453 fprintf(stderr, "%s: Initial discs must be greater than 10\n", progname);
457 if (f->maxradius <= 30) {
458 fprintf(stderr, "%s: Max radius must be greater than 30\n", progname);
462 if (f->maxrider <= 10) {
463 fprintf(stderr, "%s: Max riders must be greater than 10\n", progname);
467 XGetWindowAttributes(dpy, window, &xgwa);
469 f->height = xgwa.height;
470 f->width = xgwa.width;
471 f->visdepth = xgwa.depth;
473 gcv.foreground = get_pixel_resource("foreground", "Foreground",
475 gcv.background = get_pixel_resource("background", "Background",
477 fgc = XCreateGC(dpy, window, GCForeground, &gcv);
478 copygc = XCreateGC(dpy, window, GCForeground, &gcv);
480 f->fgcolor = gcv.foreground;
481 f->bgcolor = gcv.background;
483 /* Initialize stuff */
484 build_img(dpy, window, f);
486 for (tempx = 0; tempx < f->initial_discs; tempx++) {
487 float fx, fy, x, y, r;
490 /* Arrange in anti-collapsing circle */
491 fx = 0.4 * f->width * cos((2 * M_PI) * tempx / f->initial_discs);
492 fy = 0.4 * f->height * sin((2 * M_PI) * tempx / f->initial_discs);
493 x = frand(f->width / 2) + fx;
494 y = frand(f->height / 2) + fy;
495 r = 5 + frand(f->maxradius);
498 if ((random() % 100) < 50)
501 make_disc(f, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r);
506 if ((f->cycles % 10) == 0) {
507 /* Restart if the window size changes */
508 XGetWindowAttributes(dpy, window, &xgwa);
510 if (f->height != xgwa.height || f->width != xgwa.width) {
511 f->height = xgwa.height;
512 f->width = xgwa.width;
513 f->visdepth = xgwa.depth;
515 build_img(dpy, window, f);
519 blank_img(dpy, f->off_map, xgwa, fgc, f);
520 for (tempx = 0; tempx < f->num; tempx++) {
522 render_disc(dpy, f->off_map, fgc, f, tempx);
525 XSetFillStyle(dpy, copygc, FillTiled);
526 XSetTile(dpy, copygc, f->off_map);
527 XFillRectangle(dpy, window, copygc, 0, 0, f->width, f->height);
531 /* XSync(dpy, False); */
533 screenhack_handle_events(dpy);