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
53 #include "screenhack.h"
54 #include <X11/Xutil.h>
60 #define MAX_WIDTH SHRT_MAX
63 /* XXX take this out later */
72 /* this program goes faster if some functions are inline. The following is
73 * borrowed from ifs.c */
74 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
80 #define MIN(x,y) ((x < y) ? x : y)
84 #define MAX(x,y) ((x < y) ? y : x)
87 char *progclass = "inter-aggregate";
97 "*growthDelay: 18000",
105 XrmOptionDescRec options[] =
107 {"-background", ".background", XrmoptionSepArg, 0},
108 {"-foreground", ".foreground", XrmoptionSepArg, 0},
109 {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
110 {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
111 {"-num-circles", ".numCircles", XrmoptionSepArg, 0},
112 {"-percent-orbits", ".percentOrbits", XrmoptionSepArg, 0},
113 {"-base-orbits", ".baseOrbits", XrmoptionSepArg, 0},
114 {"-base-on-center", ".baseOnCenter", XrmoptionNoArg, "true"},
115 {"-draw-centers", ".drawCenters", XrmoptionNoArg, "true"},
119 /* Raw colormap extracted from pollockEFF.gif */
121 char *rgb_colormap[] =
123 "rgb:FF/FF/FF", /* white */
124 "rgb:00/00/00", /* black */
125 "rgb:00/00/00", /* more black */
126 /* "rgb:73/64/51", */
127 "rgb:4e/3e/2e", /* olive */
128 /* "rgb:66/66/66", */
129 "rgb:69/4d/35", /* camel */
130 "rgb:b9/a8/8c", /* tan */
135 char *rgb_colormap[] =
137 "rgb:FF/FF/FF", /* white */
138 "rgb:00/00/00", /* more black */
139 "rgb:00/00/00", /* more black */
140 "rgb:4e/3e/2e", /* olive */
141 "rgb:69/4d/35", /* camel */
142 "rgb:b0/a0/85", /* tan */
147 /* black white brown olive grey camel */
149 typedef enum { LINEAR, ORBIT } PathType;
160 typedef struct _circle
169 /* for a linear path */
173 /* for orbital path */
178 struct _circle* center;
181 SandPainter* painters;
198 /* used for orbits circling the center of the screen */
199 Circle center_of_universe;
201 /* Raw map of pixels we need to keep for alpha blending */
202 unsigned long int *off_img;
206 unsigned long *parsedcolors;
207 unsigned long fgcolor;
208 unsigned long bgcolor;
218 /* for profiling whatnot */
219 int possible_intersections;
220 int intersection_count;
227 struct field *f = (struct field*) malloc(sizeof(struct field));
230 fprintf(stderr, "%s: Failed to allocate field!\n", progname);
238 f->percent_orbits = 0;
240 f->base_on_center = False;
243 f->parsedcolors = NULL;
252 f->draw_centers = False;
254 f->possible_intersections = 0;
255 f->intersection_count = 0;
260 /* Quick references to pixels in the offscreen map and in the crack grid */
261 #define ref_pixel(f, x, y) ((f)->off_img[(y) * (f)->width + (x)])
263 #define in_bounds(f, x, y) ((x >= 0) && (x < f->width) && (y >= 0) && (y < f->height))
265 /* Consider rewriting with XQueryColor, or ImageByteOrder */
267 inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b)
274 *g = (c & 0xff00) >> 8;
275 *r = (c & 0xff0000) >> 16;
278 *b = (c & 0x1f) << 3;
279 *g = ((c >> 5) & 0x3f) << 2;
280 *r = ((c >> 11) & 0x1f) << 3;
283 *b = (c & 0x1f) << 3;
284 *g = ((c >> 5) & 0x1f) << 3;
285 *r = ((c >> 10) & 0x1f) << 3;
290 inline unsigned long rgb2point(int depth, int r, int g, int b)
292 unsigned long ret = 0;
299 ret |= (r << 16) | (g << 8) | b;
302 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
305 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
312 /* alpha blended point drawing -- this is Not Right and will likely fail on
313 * non-intel platforms as it is now, needs fixing */
314 inline unsigned long trans_point(int x1, int y1, unsigned long myc, double a,
319 ref_pixel(f, x1, y1) = myc;
324 int or=0, og=0, ob=0;
329 c = ref_pixel(f, x1, y1);
331 point2rgb(f->visdepth, c, &or, &og, &ob);
332 point2rgb(f->visdepth, myc, &r, &g, &b);
334 nr = or + (r - or) * a;
335 ng = og + (g - og) * a;
336 nb = ob + (b - ob) * a;
338 c = rgb2point(f->visdepth, nr, ng, nb);
340 ref_pixel(f, x1, y1) = c;
346 inline void drawPoint(int x, int y, unsigned long color, double intensity,
347 Display *dpy, Window window, GC fgc, struct field *f)
352 if ( x >= f->width ) x -= f->width;
353 else if ( x < 0 ) x += f->width;
355 if ( y >= f->height ) y -= f->height;
356 else if ( y < 0 ) y += f->height;
358 /* if ( in_bounds(f, x, y) ) ... */
360 c = trans_point(x, y, color, intensity, f);
362 XSetForeground(dpy, fgc, c);
363 XDrawPoint(dpy, window, fgc, x, y);
366 inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
367 Display *dpy, Window window, GC fgc,
370 /* the sand painter */
375 /* XXX try adding tpoint here, like orig */
377 /* jitter the painter's values */
378 painter->gain += frand(0.05) - 0.025;
380 if ( painter->gain > f->max_gain )
381 painter->gain = -f->max_gain;
382 else if ( painter->gain < -f->max_gain )
383 painter->gain = f->max_gain;
385 painter->p += frand(0.1) - 0.05;
387 if ( 0 < painter->p )
389 else if ( painter->p > 1.0 )
392 /* replace 0.1 with 1 / f->grains */
393 inc = painter->gain * 0.1;
396 for(i = 0; i <= 10; ++i)
400 double intensity = 0.1 - 0.009 * i;
402 sp = sin(painter->p + sandp);
403 drawx = ax + (bx - ax) * sp;
404 drawy = ay + (by - ay) * sp;
406 drawPoint(drawx, drawy, painter->color,
408 dpy, window, fgc, f);
410 sm = sin(painter->p - sandp);
411 drawx = ax + (bx - ax) * sm;
412 drawy = ay + (by - ay) * sm;
414 drawPoint(drawx, drawy, painter->color,
416 dpy, window, fgc, f);
422 void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa)
427 /* Count the colors in our map and assign them in a horrifically inefficient
428 * manner but it only happens once */
430 for( f->numcolors = 0;
431 rgb_colormap[f->numcolors] != NULL;
437 f->parsedcolors = (unsigned long *) calloc(f->numcolors,
438 sizeof(unsigned long));
439 if ( f->parsedcolors == NULL )
441 fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
446 for(i = 0; i < f->numcolors; ++i)
448 if (!XParseColor(dpy, xgwa->colormap,
449 rgb_colormap[i], &tmpcolor))
451 fprintf(stderr, "%s: couldn't parse color %s\n", progname,
456 if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor))
458 fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
463 f->parsedcolors[i] = tmpcolor.pixel;
468 /* used when the window is resized */
469 void build_img(struct field *f)
476 f->off_img = (unsigned long *) calloc(f->width * f->height,
477 sizeof(unsigned long));
480 if ( f->off_img == NULL )
482 fprintf(stderr, "%s: Failed to allocate off_img\n",
487 memset(f->off_img, f->bgcolor,
488 sizeof(unsigned long) * f->width * f->height);
491 void free_circles(struct field *f)
495 if ( f->circles != NULL )
497 for(i = 0; i < f->num_circles; ++i)
499 free (f->circles[i].painters);
507 void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc,
516 f->center_of_universe.x = f->width / 2.0;
517 f->center_of_universe.y = f->height / 2.0;
518 f->center_of_universe.r = MAX(f->width, f->height) / 2.0;
520 num_orbits = (f->percent_orbits * f->num_circles) / 100;
521 orbit_start = f->num_circles - num_orbits;
522 base_orbits = orbit_start + (num_orbits * f->base_orbits) / 100;
526 f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
527 if ( f->circles == NULL )
529 fprintf(stderr, "%s: Failed to allocate off_img\n",
534 for(i = 0; i < f->num_circles; ++i)
537 Circle *circle = f->circles + i;
539 /* make this a pref */
541 if ( i >= orbit_start )
542 circle->path_type = ORBIT;
544 circle->path_type = LINEAR;
547 if ( circle->path_type == LINEAR )
549 circle->x = frand(f->width);
550 circle->y = frand(f->height);
552 circle->dx = frand(0.5) - 0.25;
553 circle->dy = frand(0.5) - 0.25;
554 /* circle->dy = 0; */
555 /* circle->r = f->height * (0.05 + frand(0.1)); */
556 circle->radius = 5 + frand(55);
558 /* in case we want orbits based on lines */
559 circle->r = MIN(f->width, f->height) / 2.0;
560 circle->center = NULL;
564 if (i < base_orbits )
566 if ( f->base_on_center )
567 circle->center = &f->center_of_universe;
570 circle->center = f->circles +
571 ((int)frand(orbit_start - 0.1));
574 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
576 /* circle->radius = 5 + frand(55); */
580 /* give a preference for the earlier circles */
582 double p = frand(0.9);
584 circle->center = f->circles + (int) (p*i);
586 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r);
587 /* circle->r = 1 + frand(circle->center->r / 2); */
590 /* circle->radius = MAX(5, frand(circle->r)); */
591 /* circle->radius = 5 + frand(55); */
594 circle->radius = 5 + frand(MIN(55, circle->r));
595 circle->dtheta = (frand(0.5) - 0.25) / circle->r;
596 circle->theta = frand(2 * M_PI);
598 circle->x = circle->r * cos(circle->theta) + circle->center->x;
599 circle->y = circle->r * sin(circle->theta) + circle->center->y;
603 /* make this a command line option */
604 circle->num_painters = 3;
605 circle->painters = (SandPainter*) calloc(circle->num_painters,
606 sizeof(SandPainter));
607 if ( circle->painters == NULL )
609 fprintf(stderr, "%s: failed to allocate painters", progname);
613 for(j = 0; j < circle->num_painters; ++j)
615 SandPainter *painter = circle->painters + j;
617 painter->gain = frand(0.09) + 0.01;
618 painter->p = frand(1.0);
620 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
625 void moveCircles(struct field *f)
629 for(i = 0; i < f->num_circles; ++i)
631 Circle *circle = f->circles + i;
633 if ( circle->path_type == LINEAR )
635 circle->x += circle->dx;
636 circle->y += circle->dy;
639 if ( circle->x < -circle->radius )
640 circle->x = f->width + circle->radius;
641 else if ( circle->x >= f->width + circle->radius )
642 circle->x = -circle->radius;
644 if ( circle->y < -circle->radius )
645 circle->y = f->height + circle->radius;
646 else if ( circle->y >= f->height + circle->radius )
647 circle->y = -circle->radius;
649 if ( circle->x < 0 ) circle->x += f->width;
650 else if ( circle->x >= f->width ) circle->x -= f->width;
652 if ( circle->y < 0 ) circle->y += f->height;
653 else if ( circle->y >= f->height ) circle->y -= f->height;
656 else /* (circle->path_type == ORBIT) */
658 circle->theta += circle->dtheta;
660 if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
661 else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
663 circle->x = circle->r * cos(circle->theta) + circle->center->x;
664 circle->y = circle->r * sin(circle->theta) + circle->center->y;
667 if ( circle->x < -circle->radius )
668 circle->x += f->width + 2 * circle->radius;
669 else if ( circle->x >= f->width + circle->radius )
670 circle->x -= f->width + 2 * circle->radius;
672 if ( circle->y < -circle->radius )
673 circle->y += f->height + 2 * circle->radius;
674 else if ( circle->y >= f->height + circle->radius )
675 circle->y -= f->height + 2 * circle->radius;
677 if ( circle->x < 0 ) circle->x += f->width;
678 else if ( circle->x >= f->width ) circle->x -= f->width;
680 if ( circle->y < 0 ) circle->y += f->height;
681 else if ( circle->y >= f->height ) circle->y -= f->height;
687 void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
691 /* One might be tempted to think 'hey, this is a crude algorithm
692 * that is going to check each of the n (n-1) / 2 possible
693 * intersections! Why not try bsp trees, quad trees, etc, etc,
696 * In practice the time spent drawing the intersection of two
697 * circles dwarfs the time takes to check for intersection.
698 * Profiling on a 640x480 screen with 100 circles shows possible
699 * speed gains to be only a couple of percent.
701 * But hey, if you're bored, go have fun. Let me know how it
706 for(i = 0; i < f->num_circles; ++i)
708 Circle *c1 = f->circles + i;
710 if ( !f->draw_centers )
712 /* the default branch */
714 for(j = i + 1; j < f->num_circles; ++j)
716 double d, dsqr, dx, dy;
717 Circle *c2 = f->circles + j;
719 ++f->possible_intersections;
723 dsqr = dx * dx + dy * dy;
726 if ( (fabs(dx) < (c1->radius + c2->radius)) &&
727 (fabs(dy) < (c1->radius + c2->radius)) &&
728 ( d < (c1->radius + c2->radius) ) &&
729 ( d > fabs(c1->radius - c2->radius) ) )
731 double d1, d2, r1sqr;
738 /* woo-hoo. the circles are neither outside nor
739 * inside each other. they intersect.
741 * Now, compute the coordinates of the points of
745 ++f->intersection_count;
747 /* unit vector in direction of c1 to c2 */
751 r1sqr = c1->radius * c1->radius;
753 /* distance from c1's center midpoint of intersection
756 d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
758 midpx = c1->x + d1 * bx;
759 midpy = c1->y + d1 * by;
761 /* distance from midpoint to points of intersection */
763 d2 = sqrt(r1sqr - d1 * d1);
765 int1x = midpx + d2 * by;
766 int1y = midpy - d2 * bx;
768 int2x = midpx - d2 * by;
769 int2y = midpy + d2 * bx;
771 for(s = 0; s < c1->num_painters; ++s)
773 paint(c1->painters + s, int1x, int1y, int2x, int2y,
774 dpy, window, fgc, f);
779 else /* f->draw_centers */
781 XDrawPoint(dpy, window, fgc, c1->x, c1->y);
787 void screenhack(Display * dpy, Window window)
789 struct field *f = init_field();
791 unsigned int max_cycles = 0;
792 int growth_delay = 0;
796 XWindowAttributes xgwa;
800 struct timeval tm1, tm2;
804 growth_delay = (get_integer_resource("growthDelay", "Integer"));
805 max_cycles = (get_integer_resource("maxCycles", "Integer"));
806 f->num_circles = (get_integer_resource("numCircles", "Integer"));
807 f->percent_orbits = (get_integer_resource("percentOrbits", "Integer"));
808 f->base_orbits = (get_integer_resource("baseOrbits", "Integer"));
809 f->base_on_center = (get_boolean_resource("baseOnCenter", "Boolean"));
810 f->draw_centers = (get_boolean_resource("drawCenters", "Boolean"));
812 if (f->num_circles <= 1)
814 fprintf(stderr, "%s: Minimum number of circles is 2\n",
819 if ( (f->percent_orbits < 0) || (f->percent_orbits > 100) )
821 fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n",
826 if ( (f->base_orbits < 0) || (f->base_orbits > 100) )
828 fprintf(stderr, "%s: base-oribts must be between 0 and 100\n",
833 if ( f->percent_orbits == 100 )
834 f->base_on_center = True;
836 XGetWindowAttributes(dpy, window, &xgwa);
838 build_colors(f, dpy, &xgwa);
840 gcv.foreground = get_pixel_resource("foreground", "Foreground",
842 gcv.background = get_pixel_resource("background", "Background",
845 fgc = XCreateGC(dpy, window, GCForeground, &gcv);
847 f->height = xgwa.height;
848 f->width = xgwa.width;
849 f->visdepth = xgwa.depth;
850 f->fgcolor = gcv.foreground;
851 f->bgcolor = gcv.background;
853 /* Initialize stuff */
854 build_field(dpy, window, xgwa, fgc, f);
857 gettimeofday(&tm1, NULL);
863 if ((f->cycles % 10) == 0)
865 /* Restart if the window size changes */
866 XGetWindowAttributes(dpy, window, &xgwa);
868 if (f->height != xgwa.height || f->width != xgwa.width)
870 f->height = xgwa.height;
871 f->width = xgwa.width;
872 f->visdepth = xgwa.depth;
874 build_field(dpy, window, xgwa, fgc, f);
875 XSetForeground(dpy, fgc, gcv.background);
876 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
877 XSetForeground(dpy, fgc, gcv.foreground);
879 screenhack_handle_events(dpy);
883 drawIntersections(dpy, window, fgc, f);
890 screenhack_handle_events(dpy);
892 if (f->cycles >= max_cycles && max_cycles != 0)
894 build_field(dpy, window, xgwa, fgc, f);
895 XSetForeground(dpy, fgc, gcv.background);
896 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
897 XSetForeground(dpy, fgc, gcv.foreground);
901 usleep(growth_delay);
905 gettimeofday(&tm2, NULL);
907 tdiff = (tm2.tv_sec - tm1.tv_sec)
908 + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
912 fprintf(stderr, "fps: %d %f %f\n",
913 frames, tdiff, frames / tdiff );
915 fprintf(stderr, "intersections: %d %d %f\n",
916 f->intersection_count, f->possible_intersections,
917 ((double)f->intersection_count) /
918 f->possible_intersections);
920 fprintf(stderr, "fpi: %f\n",
921 ((double)frames) / f->intersection_count );
924 tm1.tv_sec = tm2.tv_sec;
925 tm1.tv_usec = tm2.tv_usec;
927 f->intersection_count = f->possible_intersections = 0;