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"
37 /* this program goes faster if some functions are inline. The following is
38 * borrowed from ifs.c */
39 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
53 /* index identifier */
76 unsigned int maxrider;
77 unsigned int maxradius;
80 unsigned long fgcolor;
81 unsigned long bgcolor;
86 /* Offscreen image we draw to */
88 unsigned char *off_alpha;
98 XWindowAttributes xgwa;
106 static void *xrealloc(void *p, size_t size)
109 if ((ret = realloc(p, size)) == NULL) {
110 fprintf(stderr, "%s: out of memory\n", progname);
116 static struct field *init_field(void)
118 struct field *f = xrealloc(NULL, sizeof(struct field));
121 f->initial_discs = 0;
134 /* Quick-ref to pixels in the alpha map */
135 #define ref_pixel(f, x, y) ((f)->off_alpha[(y) * (f)->width + (x)])
137 static inline void make_disc(struct field *f, float x, float y, float vx, float vy, float r)
139 /* Synthesis of Disc::Disc and PxRider::PxRider */
143 /* allocate a new disc */
144 f->discs = (Disc *) xrealloc(f->discs, sizeof(Disc) * (f->num + 1));
146 nd = &(f->discs[f->num]);
154 nd->r = frand(r) / 3;
156 nd->numr = (frand(r) / 2.62);
157 if (nd->numr > f->maxrider)
158 nd->numr = f->maxrider;
161 nd->pxRiders = (PxRider *) xrealloc(nd->pxRiders, sizeof(PxRider) * (f->maxrider));
162 for (ix = 0; ix < f->maxrider; ix++) {
163 nd->pxRiders[ix].vt = 0.0;
164 nd->pxRiders[ix].t = frand(M_PI * 2);
165 nd->pxRiders[ix].mycharge = 0;
170 /* alpha blended point drawing */
171 static inline unsigned long
172 trans_point(struct state *st,
173 int x1, int y1, unsigned char myc, float a, struct field *f)
175 if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
177 ref_pixel(f, x1, y1) = myc;
179 unsigned long c = ref_pixel(f, x1, y1);
180 c = c + (myc - c) * a;
181 ref_pixel(f, x1, y1) = c;
189 static inline unsigned long
190 get_pixel (struct state *st, unsigned char v)
192 return st->colors [v * (st->ncolors-1) / 255].pixel;
196 static inline void move_disc(struct field *f, int dnum)
198 Disc *d = &(f->discs[dnum]);
200 /* add velocity to position */
206 d->x += f->width + d->r + d->r;
207 if (d->x - d->r > f->width)
208 d->x -= f->width + d->r + d->r;
210 d->y += f->height + d->r + d->r;
211 if (d->y - d->r > f->height)
212 d->y -= f->height + d->r + d->r;
214 /* increase to destination radius */
220 draw_glowpoint(struct state *st, Drawable drawable,
221 GC fgc, struct field *f, float px, float py)
227 for (i =- 2; i < 3; i++) {
228 for (j =- 2; j < 3; j++) {
229 a = 0.8 - i * i * 0.1 - j * j * 0.1;
231 c = trans_point(st, px+i, py+j, 255, a, f);
232 XSetForeground(st->dpy, fgc, get_pixel (st, c));
233 XDrawPoint(st->dpy, drawable, fgc, px + i, py + j);
234 XSetForeground(st->dpy, fgc, f->fgcolor);
240 moverender_rider(struct state *st, Drawable drawable,
241 GC fgc, struct field *f, PxRider *rid,
242 float x, float y, float r)
248 /* add velocity to theta */
249 rid->t = fmod((rid->t + rid->vt + M_PI), (2 * M_PI)) - M_PI;
251 rid->vt += frand(0.002) - 0.001;
253 /* apply friction brakes */
254 if (abs(rid->vt) > 0.02)
258 px = x + r * cos(rid->t);
259 py = y + r * sin(rid->t);
261 if ((px < 0) || (px >= f->width) || (py < 0) || (py >= f->height))
264 /* max brightness seems to be 0.003845 */
266 c = ref_pixel(f, (int) px, (int) py);
269 /* guestimated - 40 is 18% of 255, so scale this to 0.0 to 0.003845 */
270 if (cv > 0.0006921) {
271 draw_glowpoint(st, drawable, fgc, f, px, py);
273 rid->mycharge = 0.003845;
275 rid->mycharge *= 0.98;
277 c = 255 * rid->mycharge;
279 trans_point(st, px, py, c, 0.5, f);
281 XSetForeground(st->dpy, fgc, get_pixel(st, c));
282 XDrawPoint(st->dpy, drawable, fgc, px, py);
283 XSetForeground(st->dpy, fgc, f->fgcolor);
288 render_disc(struct state *st, Drawable drawable, GC fgc, struct field *f, int dnum)
290 Disc *di = &(f->discs[dnum]);
293 float a, p2x, p2y, h, p3ax, p3ay, p3bx, p3by;
296 /* Find intersecting points with all ascending discs */
297 for (n = di->id + 1; n < f->num; n++) {
298 dx = f->discs[n].x - di->x;
299 dy = f->discs[n].y - di->y;
300 d = sqrt(dx * dx + dy * dy);
302 /* intersection test */
303 if (d < (f->discs[n].r + di->r)) {
304 /* complete containment test */
305 if (d > abs(f->discs[n].r - di->r)) {
307 a = (di->r * di->r - f->discs[n].r * f->discs[n].r + d * d) / (2 * d);
308 p2x = di->x + a * (f->discs[n].x - di->x) / d;
309 p2y = di->y + a * (f->discs[n].y - di->y) / d;
311 h = sqrt(di->r * di->r - a * a);
313 p3ax = p2x + h * (f->discs[n].y - di->y) / d;
314 p3ay = p2y - h * (f->discs[n].x - di->x) / d;
316 p3bx = p2x - h * (f->discs[n].y - di->y) / d;
317 p3by = p2y + h * (f->discs[n].x - di->x) / d;
320 if ((p3ax < 0) || (p3ax >= f->width) || (p3ay < 0) || (p3ay >= f->height) ||
321 (p3bx < 0) || (p3bx >= f->width) || (p3by < 0) || (p3by >= f->height))
324 /* p3a and p3b might be identical, ignore this case for now */
325 /* XPutPixel(f->off_map, p3ax, p3ay, f->fgcolor); */
326 c = trans_point(st, p3ax, p3ay, 255, 0.75, f);
327 XSetForeground(st->dpy, fgc, get_pixel (st, c));
328 XDrawPoint(st->dpy, drawable, fgc, p3ax, p3ay);
330 /* XPutPixel(f->off_map, p3bx, p3by, f->fgcolor); */
331 c = trans_point(st, p3bx, p3by, 255, 0.75, f);
332 XSetForeground(st->dpy, fgc, get_pixel (st, c));
333 XDrawPoint(st->dpy, drawable, fgc, p3bx, p3by);
334 XSetForeground(st->dpy, fgc, f->fgcolor);
340 /* Render all the pixel riders */
341 for (m = 0; m < di->numr; m++) {
342 moverender_rider(st, drawable, fgc, f, &(di->pxRiders[m]),
343 di->x, di->y, di->r);
347 static void build_img(Display *dpy, Window window, struct field *f)
353 /* Assume theres also an off pixmap */
354 if (f->off_map != window)
355 XFreePixmap(dpy, f->off_map);
358 f->off_alpha = (unsigned char *)
359 xrealloc(f->off_alpha, sizeof(unsigned char) * f->width * f->height);
361 memset(f->off_alpha, 0, sizeof(unsigned char) * f->width * f->height);
363 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
366 f->off_map = XCreatePixmap(dpy, window, f->width, f->height, f->visdepth);
371 static inline void blank_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, struct field *f)
373 memset(f->off_alpha, 0, sizeof(unsigned char) * f->width * f->height);
375 XSetForeground(dpy, fgc, f->bgcolor);
376 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
377 XSetForeground(dpy, fgc, f->fgcolor);
382 intermomentary_init (Display *dpy, Window window)
384 struct state *st = (struct state *) calloc (1, sizeof(*st));
387 time_t start_time = time(NULL);
396 XGetWindowAttributes(dpy, window, &st->xgwa);
398 st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
400 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
402 gcv.foreground = get_pixel_resource(dpy, st->xgwa.colormap,
403 "foreground", "Foreground");
404 gcv.background = get_pixel_resource(dpy, st->xgwa.colormap,
405 "background", "Background");
410 double fgs, fgv, bgs, bgv;
411 fgc.pixel = gcv.foreground;
412 bgc.pixel = gcv.background;
413 XQueryColor (st->dpy, st->xgwa.colormap, &fgc);
414 XQueryColor (st->dpy, st->xgwa.colormap, &bgc);
415 rgb_to_hsv (fgc.red, fgc.green, fgc.blue, &fgh, &fgs, &fgv);
416 rgb_to_hsv (bgc.red, bgc.green, bgc.blue, &bgh, &bgs, &bgv);
422 make_color_ramp (st->dpy, st->xgwa.colormap,
425 st->colors, &st->ncolors,
430 st->f = init_field();
432 st->f->height = st->xgwa.height;
433 st->f->width = st->xgwa.width;
434 st->f->visdepth = st->xgwa.depth;
436 st->draw_delay = (get_integer_resource(dpy, "drawDelay", "Integer"));
437 st->f->maxrider = (get_integer_resource(dpy, "maxRiders", "Integer"));
438 st->f->maxradius = (get_integer_resource(dpy, "maxRadius", "Integer"));
439 st->f->initial_discs = (get_integer_resource(dpy, "numDiscs", "Integer"));
441 if (st->f->initial_discs <= 10) {
442 fprintf(stderr, "%s: Initial discs must be greater than 10\n", progname);
446 if (st->f->maxradius <= 30) {
447 fprintf(stderr, "%s: Max radius must be greater than 30\n", progname);
451 if (st->f->maxrider <= 10) {
452 fprintf(stderr, "%s: Max riders must be greater than 10\n", progname);
456 st->fgc = XCreateGC(dpy, window, GCForeground, &gcv);
457 st->copygc = XCreateGC(dpy, window, GCForeground, &gcv);
459 st->f->fgcolor = gcv.foreground;
460 st->f->bgcolor = gcv.background;
462 /* Initialize stuff */
463 build_img(dpy, window, st->f);
465 for (tempx = 0; tempx < st->f->initial_discs; tempx++) {
466 float fx, fy, x, y, r;
469 /* Arrange in anti-collapsing circle */
470 fx = 0.4 * st->f->width * cos((2 * M_PI) * tempx / st->f->initial_discs);
471 fy = 0.4 * st->f->height * sin((2 * M_PI) * tempx / st->f->initial_discs);
472 x = frand(st->f->width / 2) + fx;
473 y = frand(st->f->height / 2) + fy;
474 r = 5 + frand(st->f->maxradius);
477 if ((random() % 100) < 50)
480 make_disc(st->f, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r);
488 intermomentary_draw (Display *dpy, Window window, void *closure)
490 struct state *st = (struct state *) closure;
493 if ((st->f->cycles % 10) == 0) {
494 /* Restart if the window size changes */
495 XGetWindowAttributes(dpy, window, &st->xgwa);
497 if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) {
498 st->f->height = st->xgwa.height;
499 st->f->width = st->xgwa.width;
500 st->f->visdepth = st->xgwa.depth;
502 build_img(dpy, window, st->f);
506 blank_img(dpy, st->f->off_map, st->xgwa, st->fgc, st->f);
507 for (tempx = 0; tempx < st->f->num; tempx++) {
508 move_disc(st->f, tempx);
509 render_disc(st, st->f->off_map, st->fgc, st->f, tempx);
513 XSetFillStyle(dpy, st->copygc, FillTiled);
514 XSetTile(dpy, st->copygc, st->f->off_map);
515 XFillRectangle(dpy, window, st->copygc, 0, 0, st->f->width, st->f->height);
517 if (st->f->off_map != window)
518 XCopyArea (dpy, st->f->off_map, window, st->copygc, 0, 0,
519 st->f->width, st->f->height, 0, 0);
525 return st->draw_delay;
530 intermomentary_reshape (Display *dpy, Window window, void *closure,
531 unsigned int w, unsigned int h)
536 intermomentary_event (Display *dpy, Window window, void *closure, XEvent *event)
542 intermomentary_free (Display *dpy, Window window, void *closure)
544 struct state *st = (struct state *) closure;
549 static const char *intermomentary_defaults[] = {
550 ".background: black",
551 ".foreground: yellow",
560 static XrmOptionDescRec intermomentary_options[] = {
561 {"-background", ".background", XrmoptionSepArg, 0},
562 {"-foreground", ".foreground", XrmoptionSepArg, 0},
563 {"-draw-delay", ".drawDelay", XrmoptionSepArg, 0},
564 {"-num-discs", ".numDiscs", XrmoptionSepArg, 0},
565 {"-max-riders", ".maxRiders", XrmoptionSepArg, 0},
566 {"-max-radius", ".maxRadius", XrmoptionSepArg, 0},
567 { "-colors", ".colors", XrmoptionSepArg, 0 },
572 XSCREENSAVER_MODULE ("Intermomentary", intermomentary)