2 * InterAggregate (dagraz@gmail.com)
3 * Based on code from complexification.net Intersection Aggregate
4 * http://www.complexification.net/gallery/machines/interAggregate/index.php
6 * Intersection Aggregate code:
8 * Albuquerque, New Mexico
11 * Also based on substrate, a port of j.tarbell's Substrate Art done
12 * by dragorn@kismetwireless.net
15 * Interesting command line options, all of which serve to
16 * concentrate the painting in some way:
18 * -percent-orbits 100 -base-orbits 50 -base-on-center -growth-delay 0
20 * Paint should be concentrated in the center of the canvas, orbiting
21 * about it. -percent-orbits 100 implies -base-on-center, so that's
25 * -percent-orbits 99 -base-orbits 50 -growth-delay 0
27 * Like the above example, but the 'center' will rove about the screen.
29 * -percent-orbits 98 -base-orbits 50 -growth-delay 0
31 * Like the above example, but there will be two roving centers.
35 * -fix alpha blending / byte ordering
40 * Directly based the hacks of:
42 * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
44 * Permission to use, copy, modify, distribute, and sell this software and its
45 * documentation for any purpose is hereby granted without fee, provided that
46 * the above copyright notice appear in all copies and that both that
47 * copyright notice and this permission notice appear in supporting
48 * documentation. No representations are made about the suitability of this
49 * software for any purpose. It is provided "as is" without express or
54 #include "screenhack.h"
57 /* this program goes faster if some functions are inline. The following is
58 * borrowed from ifs.c */
59 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
65 #define MIN(x,y) ((x < y) ? x : y)
69 #define MAX(x,y) ((x < y) ? y : x)
72 static const char *interaggregate_defaults[] =
81 "*growthDelay: 18000",
86 "*baseOnCenter: False",
87 "*drawCenters: False",
89 "*ignoreRotation: True",
94 static XrmOptionDescRec interaggregate_options[] =
96 {"-background", ".background", XrmoptionSepArg, 0},
97 {"-foreground", ".foreground", XrmoptionSepArg, 0},
98 {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
99 {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
100 {"-num-circles", ".numCircles", XrmoptionSepArg, 0},
101 {"-percent-orbits", ".percentOrbits", XrmoptionSepArg, 0},
102 {"-base-orbits", ".baseOrbits", XrmoptionSepArg, 0},
103 {"-base-on-center", ".baseOnCenter", XrmoptionNoArg, "true"},
104 {"-draw-centers", ".drawCenters", XrmoptionNoArg, "true"},
108 /* Raw colormap extracted from pollockEFF.gif */
110 char *rgb_colormap[] =
112 "#FFFFFF", /* white */
113 "#000000", /* black */
114 "#000000", /* more black */
116 "#4e3e2e", /* olive */
118 "#694d35", /* camel */
124 static const char *rgb_colormap[] =
126 "#FFFFFF", /* white */
127 "#000000", /* more black */
128 "#000000", /* more black */
129 "#4e3e2e", /* olive */
130 "#694d35", /* camel */
136 /* black white brown olive grey camel */
138 typedef enum { LINEAR, ORBIT } PathType;
149 typedef struct _circle
158 /* for a linear path */
162 /* for orbital path */
167 struct _circle* center;
170 SandPainter* painters;
187 /* used for orbits circling the center of the screen */
188 Circle center_of_universe;
190 /* Raw map of pixels we need to keep for alpha blending */
191 unsigned long int *off_img;
195 unsigned long *parsedcolors;
196 unsigned long fgcolor;
197 unsigned long bgcolor;
207 /* for profiling whatnot */
208 int possible_intersections;
209 int intersection_count;
213 static struct field *
216 struct field *f = (struct field*) malloc(sizeof(struct field));
219 fprintf(stderr, "%s: Failed to allocate field!\n", progname);
227 f->percent_orbits = 0;
229 f->base_on_center = False;
232 f->parsedcolors = NULL;
241 f->draw_centers = False;
243 f->possible_intersections = 0;
244 f->intersection_count = 0;
249 /* Quick references to pixels in the offscreen map and in the crack grid */
250 #define ref_pixel(f, x, y) ((f)->off_img[(y) * (f)->width + (x)])
252 #define in_bounds(f, x, y) ((x >= 0) && (x < f->width) && (y >= 0) && (y < f->height))
254 /* Consider rewriting with XQueryColor, or ImageByteOrder */
256 static inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b)
263 /* This program idiotically does not go through a color map, so
264 we have to hardcode in knowledge of how jwxyz.a packs pixels!
265 Fix it to go through st->colors[st->ncolors] instead!
267 *r = (c & 0x00ff0000) >> 16;
268 *g = (c & 0x0000ffff) >> 8;
269 *b = (c & 0x000000ff);
272 *g = (c & 0xff00) >> 8;
273 *r = (c & 0xff0000) >> 16;
277 *b = (int) (c & 0x1f) << 3;
278 *g = (int) ((c >> 5) & 0x3f) << 2;
279 *r = (int) ((c >> 11) & 0x1f) << 3;
282 *b = (int) (c & 0x1f) << 3;
283 *g = (int) ((c >> 5) & 0x1f) << 3;
284 *r = (int) ((c >> 10) & 0x1f) << 3;
289 static inline unsigned long rgb2point(int depth, int r, int g, int b)
291 unsigned long ret = 0;
298 /* This program idiotically does not go through a color map, so
299 we have to hardcode in knowledge of how jwxyz.a packs pixels!
300 Fix it to go through st->colors[st->ncolors] instead!
302 ret = 0xFF000000 | (r << 16) | (g << 8) | b;
304 ret |= (r << 16) | (g << 8) | b;
308 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
311 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
318 /* alpha blended point drawing -- this is Not Right and will likely fail on
319 * non-intel platforms as it is now, needs fixing */
320 static inline unsigned long trans_point(int x1, int y1, unsigned long myc, double a,
325 ref_pixel(f, x1, y1) = myc;
330 int or=0, og=0, ob=0;
335 c = ref_pixel(f, x1, y1);
337 point2rgb(f->visdepth, c, &or, &og, &ob);
338 point2rgb(f->visdepth, myc, &r, &g, &b);
340 nr = or + (r - or) * a;
341 ng = og + (g - og) * a;
342 nb = ob + (b - ob) * a;
344 c = rgb2point(f->visdepth, nr, ng, nb);
346 ref_pixel(f, x1, y1) = c;
352 static inline void drawPoint(int x, int y, unsigned long color, double intensity,
353 Display *dpy, Window window, GC fgc, struct field *f)
358 while ( x >= f->width ) x -= f->width;
359 while ( x < 0 ) x += f->width;
361 while ( y >= f->height ) y -= f->height;
362 while ( y < 0 ) y += f->height;
364 /* if ( in_bounds(f, x, y) ) ... */
366 c = trans_point(x, y, color, intensity, f);
368 XSetForeground(dpy, fgc, c);
369 XDrawPoint(dpy, window, fgc, x, y);
372 static inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
373 Display *dpy, Window window, GC fgc,
376 /* the sand painter */
381 /* XXX try adding tpoint here, like orig */
383 /* jitter the painter's values */
384 painter->gain += frand(0.05) - 0.025;
386 if ( painter->gain > f->max_gain )
387 painter->gain = -f->max_gain;
388 else if ( painter->gain < -f->max_gain )
389 painter->gain = f->max_gain;
391 painter->p += frand(0.1) - 0.05;
393 if ( 0 < painter->p )
395 else if ( painter->p > 1.0 )
398 /* replace 0.1 with 1 / f->grains */
399 inc = painter->gain * 0.1;
402 for(i = 0; i <= 10; ++i)
406 double intensity = 0.1 - 0.009 * i;
408 sp = sin(painter->p + sandp);
409 drawx = ax + (bx - ax) * sp;
410 drawy = ay + (by - ay) * sp;
412 drawPoint(drawx, drawy, painter->color,
414 dpy, window, fgc, f);
416 sm = sin(painter->p - sandp);
417 drawx = ax + (bx - ax) * sm;
418 drawy = ay + (by - ay) * sm;
420 drawPoint(drawx, drawy, painter->color,
422 dpy, window, fgc, f);
428 static void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa)
433 /* Count the colors in our map and assign them in a horrifically inefficient
434 * manner but it only happens once */
436 for( f->numcolors = 0;
437 rgb_colormap[f->numcolors] != NULL;
443 if (f->numcolors < 1) f->numcolors = 1;
444 f->parsedcolors = (unsigned long *) calloc(f->numcolors,
445 sizeof(unsigned long));
446 if ( f->parsedcolors == NULL )
448 fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
453 for(i = 0; i < f->numcolors; ++i)
455 if (!XParseColor(dpy, xgwa->colormap,
456 rgb_colormap[i], &tmpcolor))
458 fprintf(stderr, "%s: couldn't parse color %s\n", progname,
463 if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor))
465 fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
470 f->parsedcolors[i] = tmpcolor.pixel;
475 /* used when the window is resized */
476 static void build_img(struct field *f)
483 f->off_img = (unsigned long *) calloc(f->width * f->height,
484 sizeof(unsigned long));
487 if ( f->off_img == NULL )
489 fprintf(stderr, "%s: Failed to allocate off_img\n",
494 memset(f->off_img, f->bgcolor,
495 sizeof(unsigned long) * f->width * f->height);
498 static void free_circles(struct field *f)
502 if ( f->circles != NULL )
504 for(i = 0; i < f->num_circles; ++i)
506 free (f->circles[i].painters);
514 static void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc,
523 f->center_of_universe.x = f->width / 2.0;
524 f->center_of_universe.y = f->height / 2.0;
525 f->center_of_universe.r = MAX(f->width, f->height) / 2.0;
527 num_orbits = (f->percent_orbits * f->num_circles) / 100;
528 orbit_start = f->num_circles - num_orbits;
529 base_orbits = orbit_start + (num_orbits * f->base_orbits) / 100;
533 f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
534 if ( f->circles == NULL )
536 fprintf(stderr, "%s: Failed to allocate off_img\n",
541 for(i = 0; i < f->num_circles; ++i)
544 Circle *circle = f->circles + i;
546 /* make this a pref */
548 if ( i >= orbit_start )
549 circle->path_type = ORBIT;
551 circle->path_type = LINEAR;
554 if ( circle->path_type == LINEAR )
556 circle->x = frand(f->width);
557 circle->y = frand(f->height);
559 circle->dx = frand(0.5) - 0.25;
560 circle->dy = frand(0.5) - 0.25;
561 /* circle->dy = 0; */
562 /* circle->r = f->height * (0.05 + frand(0.1)); */
563 circle->radius = 5 + frand(55);
565 /* in case we want orbits based on lines */
566 circle->r = MIN(f->width, f->height) / 2.0;
567 circle->center = NULL;
571 if (i < base_orbits )
573 if ( f->base_on_center )
574 circle->center = &f->center_of_universe;
577 circle->center = f->circles +
578 ((int)frand(orbit_start - 0.1));
581 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
583 /* circle->radius = 5 + frand(55); */
587 /* give a preference for the earlier circles */
589 double p = frand(0.9);
591 circle->center = f->circles + (int) (p*i);
593 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r);
594 /* circle->r = 1 + frand(circle->center->r / 2); */
597 /* circle->radius = MAX(5, frand(circle->r)); */
598 /* circle->radius = 5 + frand(55); */
601 circle->radius = 5 + frand(MIN(55, circle->r));
602 circle->dtheta = (frand(0.5) - 0.25) / circle->r;
603 circle->theta = frand(2 * M_PI);
605 circle->x = circle->r * cos(circle->theta) + circle->center->x;
606 circle->y = circle->r * sin(circle->theta) + circle->center->y;
610 /* make this a command line option */
611 circle->num_painters = 3;
612 circle->painters = (SandPainter*) calloc(circle->num_painters,
613 sizeof(SandPainter));
614 if ( circle->painters == NULL )
616 fprintf(stderr, "%s: failed to allocate painters", progname);
620 for(j = 0; j < circle->num_painters; ++j)
622 SandPainter *painter = circle->painters + j;
624 painter->gain = frand(0.09) + 0.01;
625 painter->p = frand(1.0);
627 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
632 static void moveCircles(struct field *f)
636 for(i = 0; i < f->num_circles; ++i)
638 Circle *circle = f->circles + i;
640 if ( circle->path_type == LINEAR )
642 circle->x += circle->dx;
643 circle->y += circle->dy;
646 if ( circle->x < -circle->radius )
647 circle->x = f->width + circle->radius;
648 else if ( circle->x >= f->width + circle->radius )
649 circle->x = -circle->radius;
651 if ( circle->y < -circle->radius )
652 circle->y = f->height + circle->radius;
653 else if ( circle->y >= f->height + circle->radius )
654 circle->y = -circle->radius;
656 if ( circle->x < 0 ) circle->x += f->width;
657 else if ( circle->x >= f->width ) circle->x -= f->width;
659 if ( circle->y < 0 ) circle->y += f->height;
660 else if ( circle->y >= f->height ) circle->y -= f->height;
663 else /* (circle->path_type == ORBIT) */
665 circle->theta += circle->dtheta;
667 if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
668 else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
670 circle->x = circle->r * cos(circle->theta) + circle->center->x;
671 circle->y = circle->r * sin(circle->theta) + circle->center->y;
674 if ( circle->x < -circle->radius )
675 circle->x += f->width + 2 * circle->radius;
676 else if ( circle->x >= f->width + circle->radius )
677 circle->x -= f->width + 2 * circle->radius;
679 if ( circle->y < -circle->radius )
680 circle->y += f->height + 2 * circle->radius;
681 else if ( circle->y >= f->height + circle->radius )
682 circle->y -= f->height + 2 * circle->radius;
684 if ( circle->x < 0 ) circle->x += f->width;
685 else if ( circle->x >= f->width ) circle->x -= f->width;
687 if ( circle->y < 0 ) circle->y += f->height;
688 else if ( circle->y >= f->height ) circle->y -= f->height;
694 static void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
698 /* One might be tempted to think 'hey, this is a crude algorithm
699 * that is going to check each of the n (n-1) / 2 possible
700 * intersections! Why not try bsp trees, quad trees, etc, etc,
703 * In practice the time spent drawing the intersection of two
704 * circles dwarfs the time takes to check for intersection.
705 * Profiling on a 640x480 screen with 100 circles shows possible
706 * speed gains to be only a couple of percent.
708 * But hey, if you're bored, go have fun. Let me know how it
713 for(i = 0; i < f->num_circles; ++i)
715 Circle *c1 = f->circles + i;
717 if ( !f->draw_centers )
719 /* the default branch */
721 for(j = i + 1; j < f->num_circles; ++j)
723 double d, dsqr, dx, dy;
724 Circle *c2 = f->circles + j;
727 ++f->possible_intersections;
732 dsqr = dx * dx + dy * dy;
735 if ( (fabs(dx) < (c1->radius + c2->radius)) &&
736 (fabs(dy) < (c1->radius + c2->radius)) &&
737 ( d < (c1->radius + c2->radius) ) &&
738 ( d > fabs(c1->radius - c2->radius) ) )
740 double d1, d2, r1sqr;
747 /* woo-hoo. the circles are neither outside nor
748 * inside each other. they intersect.
750 * Now, compute the coordinates of the points of
755 ++f->intersection_count;
758 /* unit vector in direction of c1 to c2 */
762 r1sqr = c1->radius * c1->radius;
764 /* distance from c1's center midpoint of intersection
767 d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
769 midpx = c1->x + d1 * bx;
770 midpy = c1->y + d1 * by;
772 /* distance from midpoint to points of intersection */
774 d2 = sqrt(r1sqr - d1 * d1);
776 int1x = midpx + d2 * by;
777 int1y = midpy - d2 * bx;
779 int2x = midpx - d2 * by;
780 int2y = midpy + d2 * bx;
782 for(s = 0; s < c1->num_painters; ++s)
784 paint(c1->painters + s, int1x, int1y, int2x, int2y,
785 dpy, window, fgc, f);
790 else /* f->draw_centers */
792 XDrawPoint(dpy, window, fgc, c1->x, c1->y);
801 unsigned int max_cycles;
805 XWindowAttributes xgwa;
812 interaggregate_init (Display *dpy, Window window)
814 struct state *st = (struct state *) calloc (1, sizeof(*st));
818 struct timeval tm1, tm2;
824 st->f = init_field();
825 st->growth_delay = (get_integer_resource(st->dpy, "growthDelay", "Integer"));
826 st->max_cycles = (get_integer_resource(st->dpy, "maxCycles", "Integer"));
827 st->f->num_circles = (get_integer_resource(st->dpy, "numCircles", "Integer"));
828 st->f->percent_orbits = (get_integer_resource(st->dpy, "percentOrbits", "Integer"));
829 st->f->base_orbits = (get_integer_resource(st->dpy, "baseOrbits", "Integer"));
830 st->f->base_on_center = (get_boolean_resource(st->dpy, "baseOnCenter", "Boolean"));
831 st->f->draw_centers = (get_boolean_resource(st->dpy, "drawCenters", "Boolean"));
833 if (st->f->num_circles <= 1)
835 fprintf(stderr, "%s: Minimum number of circles is 2\n",
840 if ( (st->f->percent_orbits < 0) || (st->f->percent_orbits > 100) )
842 fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n",
847 if ( (st->f->base_orbits < 0) || (st->f->base_orbits > 100) )
849 fprintf(stderr, "%s: base-oribts must be between 0 and 100\n",
854 if ( st->f->percent_orbits == 100 )
855 st->f->base_on_center = True;
857 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
859 build_colors(st->f, st->dpy, &st->xgwa);
861 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
862 "foreground", "Foreground");
863 st->gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
864 "background", "Background");
866 st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
868 st->f->height = st->xgwa.height;
869 st->f->width = st->xgwa.width;
870 st->f->visdepth = st->xgwa.depth;
871 st->f->fgcolor = st->gcv.foreground;
872 st->f->bgcolor = st->gcv.background;
874 /* Initialize stuff */
875 build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
878 gettimeofday(&tm1, NULL);
887 interaggregate_draw (Display *dpy, Window window, void *closure)
889 struct state *st = (struct state *) closure;
891 if ((st->f->cycles % 10) == 0)
893 /* Restart if the window size changes */
894 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
896 if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width)
898 st->f->height = st->xgwa.height;
899 st->f->width = st->xgwa.width;
900 st->f->visdepth = st->xgwa.depth;
902 build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
903 XSetForeground(st->dpy, st->fgc, st->gcv.background);
904 XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
905 XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
910 drawIntersections(st->dpy, st->window, st->fgc, st->f);
915 if (st->f->cycles >= st->max_cycles && st->max_cycles != 0)
917 build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
918 XSetForeground(st->dpy, st->fgc, st->gcv.background);
919 XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
920 XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
925 gettimeofday(&tm2, NULL);
927 tdiff = (tm2.tv_sec - tm1.tv_sec)
928 + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
932 fprintf(stderr, "fps: %d %f %f\n",
933 frames, tdiff, frames / tdiff );
935 fprintf(stderr, "intersections: %d %d %f\n",
936 f->intersection_count, f->possible_intersections,
937 ((double)f->intersection_count) /
938 f->possible_intersections);
940 fprintf(stderr, "fpi: %f\n",
941 ((double)frames) / f->intersection_count );
944 tm1.tv_sec = tm2.tv_sec;
945 tm1.tv_usec = tm2.tv_usec;
947 f->intersection_count = f->possible_intersections = 0;
951 return st->growth_delay;
956 interaggregate_reshape (Display *dpy, Window window, void *closure,
957 unsigned int w, unsigned int h)
962 interaggregate_event (Display *dpy, Window window, void *closure, XEvent *event)
964 struct state *st = (struct state *) closure;
965 if (screenhack_event_helper (dpy, window, event))
967 st->f->height--; /* act like a resize */
974 interaggregate_free (Display *dpy, Window window, void *closure)
976 struct state *st = (struct state *) closure;
981 XSCREENSAVER_MODULE ("Interaggregate", interaggregate)