986acd5327a0292b23bdf5e52b1cd7efe1a6b211
[xscreensaver] / hacks / interaggregate.c
1 /*
2  *  InterAggregate (dagraz@gmail.com)
3  *  Based on code from complexification.net Intersection Aggregate
4  *  http://www.complexification.net/gallery/machines/interAggregate/index.php
5  *
6  *  Intersection Aggregate code:
7  *  j.tarbell   May, 2004
8  *  Albuquerque, New Mexico
9  *  complexification.net
10  *
11  *  Also based on substrate, a port of j.tarbell's Substrate Art done
12  *  by dragorn@kismetwireless.net
13  *
14  *  
15  *  Interesting command line options, all of which serve to
16  *  concentrate the painting in some way:
17  *  
18  *  -percent-orbits 100 -base-orbits 50 -base-on-center -growth-delay 0
19  *
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
22  *  not really needed.
23  *
24  *
25  *  -percent-orbits 99 -base-orbits 50 -growth-delay 0
26  *
27  *  Like the above example, but the 'center' will rove about the screen.
28  *
29  *  -percent-orbits 98 -base-orbits 50 -growth-delay 0
30  *
31  *  Like the above example, but there will be two roving centers.
32  *
33  *
34  *  TODO:
35  *  -fix alpha blending / byte ordering
36  *
37  *  CHANGES
38  *
39  *
40  * Directly based the hacks of: 
41  * 
42  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
43  *
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 
50  * implied warranty.
51  */
52
53 #include <math.h>
54 #include "screenhack.h"
55
56
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)
60 #undef inline
61 #define inline                  /* */
62 #endif
63
64 #ifndef MIN
65 #define MIN(x,y) ((x < y) ? x : y)
66 #endif
67
68 #ifndef MAX
69 #define MAX(x,y) ((x < y) ? y : x)
70 #endif
71
72 static const char *interaggregate_defaults[] = 
73 {
74     ".background: white",
75     ".foreground: black",
76     "*fpsSolid: true",
77     "*maxCycles: 100000",
78 #ifdef TIME_ME
79     "*growthDelay: 0",
80 #else
81     "*growthDelay: 18000", 
82 #endif
83     "*numCircles: 100",
84     "*percentOrbits: 0",
85     "*baseOrbits: 75",
86     "*baseOnCenter: False",
87     "*drawCenters: False",
88     0
89 };
90
91 static XrmOptionDescRec interaggregate_options[] = 
92 {
93     {"-background", ".background", XrmoptionSepArg, 0},
94     {"-foreground", ".foreground", XrmoptionSepArg, 0},
95     {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
96     {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
97     {"-num-circles", ".numCircles", XrmoptionSepArg, 0},
98     {"-percent-orbits", ".percentOrbits", XrmoptionSepArg, 0},
99     {"-base-orbits", ".baseOrbits", XrmoptionSepArg, 0},
100     {"-base-on-center", ".baseOnCenter", XrmoptionNoArg, "true"}, 
101     {"-draw-centers", ".drawCenters", XrmoptionNoArg, "true"}, 
102     {0, 0, 0, 0}
103 };
104
105 /* Raw colormap extracted from pollockEFF.gif */
106 #if 0
107 char *rgb_colormap[] = 
108 {
109     "#FFFFFF", /* white */
110     "#000000", /* black */
111     "#000000", /* more black */
112     /* "#736451",  */
113     "#4e3e2e", /* olive */
114     /* "#666666", */
115     "#694d35",  /* camel */
116     "#b9a88c",  /* tan */
117     0
118 };
119 #endif
120
121 static const char *rgb_colormap[] = 
122 {
123     "#FFFFFF", /* white */
124     "#000000", /* more black */
125     "#000000", /* more black */
126     "#4e3e2e", /* olive */
127     "#694d35",  /* camel */
128     "#b0a085",  /* tan */
129     "#e6d3ae",
130     0
131 };
132
133 /* black white brown olive grey camel */
134
135 typedef enum { LINEAR, ORBIT } PathType;
136
137 typedef struct 
138 {
139
140     unsigned long color;
141     double gain;
142     double p;
143
144 } SandPainter;
145
146 typedef struct _circle
147 {
148     double radius;
149
150     double x;
151     double y;
152
153     PathType path_type;
154
155     /* for a linear path */
156     double dx;
157     double dy;
158
159     /* for orbital path */
160     double theta;
161     double r; 
162     double dtheta;
163     
164     struct _circle* center;
165
166     int num_painters;
167     SandPainter* painters;
168
169 } Circle;
170
171
172 struct field 
173 {
174     int height;
175     int width;
176
177     int num_circles;
178     Circle* circles;
179
180     int percent_orbits;
181     int base_orbits;
182     Bool base_on_center;
183
184     /* used for orbits circling the center of the screen */
185     Circle center_of_universe;
186
187     /* Raw map of pixels we need to keep for alpha blending */
188     unsigned long int *off_img;
189    
190     /* color parms */
191     int numcolors;
192     unsigned long *parsedcolors;
193     unsigned long fgcolor;
194     unsigned long bgcolor;
195     int visdepth;
196
197     unsigned int cycles;
198
199     double max_gain;
200
201     /* for debugging */
202     Bool draw_centers;
203
204     /* for profiling whatnot */ 
205     int possible_intersections;
206     int intersection_count;
207 };
208
209
210 static struct field *
211 init_field(void)
212 {
213     struct field *f = (struct field*) malloc(sizeof(struct field));
214     if ( f == NULL )
215     {
216         fprintf(stderr, "%s: Failed to allocate field!\n", progname);
217         exit(1);
218     }
219
220     f->height = 0;
221     f->width = 0;
222     f->num_circles = 0;
223     f->circles = NULL;
224     f->percent_orbits = 0;
225     f->base_orbits = 0;
226     f->base_on_center = False;
227     f->off_img = NULL;
228     f->numcolors = 0;
229     f->parsedcolors = NULL;
230     f->fgcolor = 0;
231     f->bgcolor = 0;
232     f->visdepth = 0;
233
234     f->cycles = 0;
235
236     f->max_gain = 0.22;
237
238     f->draw_centers = False;
239
240     f->possible_intersections = 0;
241     f->intersection_count = 0;
242
243     return f;
244 }
245
246 /* Quick references to pixels in the offscreen map and in the crack grid */
247 #define ref_pixel(f, x, y)   ((f)->off_img[(y) * (f)->width + (x)])
248
249 #define in_bounds(f, x, y) ((x >= 0) && (x < f->width) && (y >= 0) && (y < f->height))
250
251 /* Consider rewriting with XQueryColor, or ImageByteOrder */
252
253 static inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) 
254 {
255     switch(depth) 
256     {
257     case 32:
258     case 24:
259 #ifdef HAVE_COCOA
260         /* This program idiotically does not go through a color map, so
261            we have to hardcode in knowledge of how jwxyz.a packs pixels!
262            Fix it to go through st->colors[st->ncolors] instead!
263          */
264         *r = (c & 0x00ff0000) >> 16; 
265         *g = (c & 0x0000ffff) >>  8;
266         *b = (c & 0x000000ff);
267 #else
268         *b = c & 0xff; 
269         *g = (c & 0xff00) >> 8; 
270         *r = (c & 0xff0000) >> 16; 
271 #endif
272         break;
273     case 16:
274         *b = (c & 0x1f) << 3; 
275         *g = ((c >> 5) & 0x3f) << 2;
276         *r = ((c >> 11) & 0x1f) << 3; 
277         break;
278     case 15:
279         *b = (c & 0x1f) << 3;
280         *g = ((c >> 5) & 0x1f) << 3;
281         *r = ((c >> 10) & 0x1f) << 3;
282         break;
283     }
284 }
285
286 static inline unsigned long rgb2point(int depth, int r, int g, int b) 
287 {
288     unsigned long ret = 0;
289
290     switch(depth) 
291     {
292     case 32:
293         ret = 0xff000000;
294     case 24:
295 #ifdef HAVE_COCOA
296         /* This program idiotically does not go through a color map, so
297            we have to hardcode in knowledge of how jwxyz.a packs pixels!
298            Fix it to go through st->colors[st->ncolors] instead!
299          */
300         ret = 0xFF000000 | (r << 16) | (g << 8) | b;
301 #else
302         ret |= (r << 16) | (g << 8) | b;
303 #endif
304         break;
305     case 16:
306         ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
307         break;
308     case 15:
309         ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
310         break;
311     }
312
313     return ret;
314 }
315
316 /* alpha blended point drawing -- this is Not Right and will likely fail on 
317  * non-intel platforms as it is now, needs fixing */
318 static inline unsigned long trans_point(int x1, int y1, unsigned long myc, double a, 
319                                  struct field *f) 
320 {
321     if (a >= 1.0) 
322     {
323         ref_pixel(f, x1, y1) = myc;
324         return myc;
325     } 
326     else 
327     {
328         int or=0, og=0, ob=0;
329         int r=0, g=0, b=0;
330         int nr, ng, nb;
331         unsigned long c;
332
333         c = ref_pixel(f, x1, y1);
334
335         point2rgb(f->visdepth, c, &or, &og, &ob);
336         point2rgb(f->visdepth, myc, &r, &g, &b);
337
338         nr = or + (r - or) * a;
339         ng = og + (g - og) * a;
340         nb = ob + (b - ob) * a;
341
342         c = rgb2point(f->visdepth, nr, ng, nb);
343
344         ref_pixel(f, x1, y1) = c;
345
346         return c;
347     }
348 }
349
350 static inline void drawPoint(int x, int y, unsigned long color, double intensity,
351                       Display *dpy, Window window, GC fgc, struct field *f)
352                
353 {
354     unsigned long c;
355
356     while ( x >= f->width ) x -= f->width;
357     while ( x < 0 ) x += f->width;
358         
359     while ( y >= f->height ) y -= f->height;
360     while ( y < 0 ) y += f->height;
361
362     /* if ( in_bounds(f, x, y) ) ... */
363
364     c = trans_point(x, y, color, intensity, f);
365
366     XSetForeground(dpy, fgc, c);
367     XDrawPoint(dpy, window, fgc, x, y);
368 }
369
370 static inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
371                   Display *dpy, Window window, GC fgc, 
372                   struct field *f)
373 {
374     /* the sand painter */
375
376     double inc, sandp;
377     int i;
378
379     /* XXX try adding tpoint here, like orig */
380
381     /* jitter the painter's values */
382     painter->gain += frand(0.05) - 0.025;
383     
384     if ( painter->gain > f->max_gain )
385         painter->gain = -f->max_gain;
386     else if ( painter->gain < -f->max_gain )
387         painter->gain = f->max_gain;
388
389     painter->p += frand(0.1) - 0.05;
390     
391     if ( 0 < painter->p )
392         painter->p = 0;
393     else if ( painter->p > 1.0 )
394         painter->p = 1.0;
395
396     /* replace 0.1 with 1 / f->grains */
397     inc = painter->gain * 0.1;
398     sandp = 0;
399
400     for(i = 0; i <= 10; ++i)
401     {
402         int drawx, drawy;
403         double sp, sm;
404         double intensity = 0.1 - 0.009 * i;
405
406         sp = sin(painter->p + sandp);
407         drawx = ax + (bx - ax) * sp;
408         drawy = ay + (by - ay) * sp;
409
410         drawPoint(drawx, drawy, painter->color,
411                   intensity,
412                   dpy, window, fgc, f);
413
414         sm = sin(painter->p - sandp);
415         drawx = ax + (bx - ax) * sm;
416         drawy = ay + (by - ay) * sm;
417
418         drawPoint(drawx, drawy, painter->color,
419                   intensity,
420                   dpy, window, fgc, f);
421
422         sandp += inc;
423     }
424 }
425
426 static void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa) 
427 {
428
429     XColor tmpcolor;
430     int i;
431     /* Count the colors in our map and assign them in a horrifically inefficient 
432      * manner but it only happens once */
433
434     for( f->numcolors = 0; 
435          rgb_colormap[f->numcolors] != NULL; 
436          ++f->numcolors )
437     {
438         ;
439     }
440
441     f->parsedcolors = (unsigned long *) calloc(f->numcolors,
442                                                sizeof(unsigned long));
443     if ( f->parsedcolors == NULL )
444     {
445         fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
446                 progname);
447         exit(1);
448     }
449         
450     for(i = 0; i < f->numcolors; ++i)
451     {
452         if (!XParseColor(dpy, xgwa->colormap, 
453                          rgb_colormap[i], &tmpcolor)) 
454         {
455             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
456                     rgb_colormap[i]);
457             exit(1);
458         }
459
460         if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor)) 
461         {
462             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
463                     rgb_colormap[i]);
464             exit(1);
465         }
466
467         f->parsedcolors[i] = tmpcolor.pixel;
468
469     }
470 }
471
472 /* used when the window is resized */
473 static void build_img(struct field *f)
474 {
475     if (f->off_img) {
476         free(f->off_img);
477         f->off_img = NULL;
478     }
479
480     f->off_img = (unsigned long *) calloc(f->width * f->height,
481                                           sizeof(unsigned long));
482                                            
483
484     if ( f->off_img == NULL )
485     {
486         fprintf(stderr, "%s: Failed to allocate off_img\n",
487                 progname);
488         exit(1);
489     }
490
491     memset(f->off_img, f->bgcolor, 
492            sizeof(unsigned long) * f->width * f->height);
493 }
494
495 static void free_circles(struct field *f) 
496 {
497     int i;
498
499     if ( f->circles != NULL )
500     {
501         for(i = 0; i < f->num_circles; ++i)
502         {
503             free (f->circles[i].painters);
504         }
505
506         free (f->circles);
507         f->circles = NULL;
508     }
509 }
510
511 static void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
512                  struct field *f) 
513 {
514     int i;
515     int num_orbits;
516     int base_orbits;
517     int orbit_start;
518     build_img(f);
519
520     f->center_of_universe.x = f->width / 2.0;
521     f->center_of_universe.y = f->height / 2.0;
522     f->center_of_universe.r = MAX(f->width, f->height) / 2.0;
523
524     num_orbits = (f->percent_orbits * f->num_circles) / 100;
525     orbit_start = f->num_circles - num_orbits;
526     base_orbits = orbit_start + (num_orbits * f->base_orbits) / 100;
527
528     free_circles(f);
529
530     f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
531     if ( f->circles == NULL )
532     {
533         fprintf(stderr, "%s: Failed to allocate off_img\n",
534                 progname);
535         exit(1);
536     }
537
538     for(i = 0; i < f->num_circles; ++i)
539     {
540         int j;
541         Circle *circle = f->circles + i;
542
543         /* make this a pref */
544
545         if ( i >= orbit_start )
546           circle->path_type = ORBIT;
547         else
548           circle->path_type = LINEAR;
549
550
551         if ( circle->path_type == LINEAR )
552         {
553             circle->x = frand(f->width);
554             circle->y = frand(f->height);
555
556             circle->dx = frand(0.5) - 0.25; 
557             circle->dy = frand(0.5) - 0.25;
558             /* circle->dy = 0; */
559             /* circle->r  = f->height * (0.05 + frand(0.1)); */
560             circle->radius = 5 + frand(55);
561
562             /* in case we want orbits based on lines */
563             circle->r = MIN(f->width, f->height) / 2.0;
564             circle->center = NULL;
565         }
566         else /* == ORBIT */
567         {
568             if (i < base_orbits )
569             {
570                 if ( f->base_on_center )
571                     circle->center = &f->center_of_universe; 
572                 else
573                 {
574                     circle->center = f->circles + 
575                         ((int)frand(orbit_start - 0.1));
576                 }
577
578                 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
579
580                 /* circle->radius = 5 + frand(55); */
581             }
582             else
583             {
584                 /* give a preference for the earlier circles */
585
586                 double p = frand(0.9);
587
588                 circle->center = f->circles + (int) (p*i);
589
590                 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r); 
591                 /* circle->r = 1 + frand(circle->center->r / 2); */
592
593
594                 /* circle->radius = MAX(5, frand(circle->r)); */
595                 /* circle->radius = 5 + frand(55); */
596             }
597
598             circle->radius = 5 + frand(MIN(55, circle->r)); 
599             circle->dtheta = (frand(0.5) - 0.25) / circle->r;
600             circle->theta = frand(2 * M_PI);
601
602             circle->x = circle->r * cos(circle->theta) + circle->center->x;
603             circle->y = circle->r * sin(circle->theta) + circle->center->y;
604
605         }
606
607         /* make this a command line option */
608         circle->num_painters = 3;
609         circle->painters = (SandPainter*) calloc(circle->num_painters, 
610                                                  sizeof(SandPainter));
611         if ( circle->painters == NULL )
612         {
613             fprintf(stderr, "%s: failed to allocate painters", progname);
614             exit(1);
615         }
616
617         for(j = 0; j < circle->num_painters; ++j)
618         {
619             SandPainter *painter = circle->painters + j;
620
621             painter->gain = frand(0.09) + 0.01;
622             painter->p = frand(1.0);
623             painter->color = 
624                 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
625         }
626     }
627 }
628
629 static void moveCircles(struct field *f)
630 {
631     int i;
632
633     for(i = 0; i < f->num_circles; ++i)
634     {
635         Circle *circle = f->circles + i;
636
637         if ( circle->path_type == LINEAR )
638         {
639             circle->x += circle->dx;
640             circle->y += circle->dy;
641
642 #if 0
643             if ( circle->x < -circle->radius )
644                 circle->x = f->width + circle->radius;
645             else if ( circle->x >= f->width + circle->radius )
646                 circle->x = -circle->radius;
647
648             if ( circle->y < -circle->radius )
649                 circle->y = f->height + circle->radius;
650             else if ( circle->y >= f->height + circle->radius )
651                 circle->y = -circle->radius;
652 #else
653             if ( circle->x < 0 ) circle->x += f->width;
654             else if ( circle->x >= f->width ) circle->x -= f->width;
655
656             if ( circle->y < 0 ) circle->y += f->height;
657             else if ( circle->y >= f->height ) circle->y -= f->height;
658 #endif
659         }
660         else /* (circle->path_type == ORBIT) */
661         {
662             circle->theta += circle->dtheta;
663
664             if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
665             else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
666
667             circle->x = circle->r * cos(circle->theta) + circle->center->x;
668             circle->y = circle->r * sin(circle->theta) + circle->center->y;
669
670 #if 0
671             if ( circle->x < -circle->radius )
672                 circle->x += f->width + 2 * circle->radius;
673             else if ( circle->x >= f->width + circle->radius )
674                 circle->x -= f->width + 2 * circle->radius;
675
676             if ( circle->y < -circle->radius )
677                 circle->y += f->height + 2 * circle->radius;
678             else if ( circle->y >= f->height + circle->radius )
679                 circle->y -= f->height + 2 * circle->radius;
680 #else
681             if ( circle->x < 0 ) circle->x += f->width;
682             else if ( circle->x >= f->width ) circle->x -= f->width;
683
684             if ( circle->y < 0 ) circle->y += f->height;
685             else if ( circle->y >= f->height ) circle->y -= f->height;
686 #endif
687         }
688     }
689 }
690
691 static void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
692 {
693     int i,j;
694
695     /* One might be tempted to think 'hey, this is a crude algorithm
696      * that is going to check each of the n (n-1) / 2 possible
697      * intersections!  Why not try bsp trees, quad trees, etc, etc,
698      * etc'
699      *
700      * In practice the time spent drawing the intersection of two
701      * circles dwarfs the time takes to check for intersection.
702      * Profiling on a 640x480 screen with 100 circles shows possible
703      * speed gains to be only a couple of percent.
704      * 
705      * But hey, if you're bored, go have fun.  Let me know how it
706      * turns out.
707      */
708
709
710     for(i = 0; i < f->num_circles; ++i)
711     {
712         Circle *c1 = f->circles + i;
713
714         if ( !f->draw_centers )
715         {
716             /* the default branch */
717
718             for(j = i + 1; j < f->num_circles; ++j)
719             {
720                 double d, dsqr, dx, dy;
721                 Circle *c2 = f->circles + j;
722
723 #ifdef TIME_ME
724                 ++f->possible_intersections;
725 #endif
726                 dx = c2->x - c1->x;
727                 dy = c2->y - c1->y;
728
729                 dsqr = dx * dx + dy * dy;
730                 d = sqrt(dsqr);
731
732                 if ( (fabs(dx) < (c1->radius + c2->radius)) &&
733                      (fabs(dy) < (c1->radius + c2->radius)) &&
734                      ( d < (c1->radius + c2->radius) ) &&
735                      ( d > fabs(c1->radius - c2->radius) ) )
736                 {
737                     double d1, d2, r1sqr; 
738                     double bx, by;
739                     double midpx, midpy;
740                     double int1x, int1y;
741                     double int2x, int2y;
742                     int s;
743
744                     /* woo-hoo.  the circles are neither outside nor
745                      * inside each other.  they intersect.  
746                      *
747                      * Now, compute the coordinates of the points of
748                      * intersection 
749                      */
750
751 #ifdef TIME_ME
752                     ++f->intersection_count;
753 #endif
754
755                     /* unit vector in direction of c1 to c2 */
756                     bx = dx / d;
757                     by = dy / d;
758
759                     r1sqr = c1->radius * c1->radius;
760
761                     /* distance from c1's center midpoint of intersection
762                      * points */
763
764                     d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
765                 
766                     midpx = c1->x + d1 * bx;
767                     midpy = c1->y + d1 * by;
768
769                     /* distance from midpoint to points of intersection */
770
771                     d2 = sqrt(r1sqr - d1 * d1);
772
773                     int1x = midpx + d2 * by;
774                     int1y = midpy - d2 * bx;
775
776                     int2x = midpx - d2 * by;
777                     int2y = midpy + d2 * bx;
778
779                     for(s = 0; s < c1->num_painters; ++s)
780                     {
781                         paint(c1->painters + s, int1x, int1y, int2x, int2y, 
782                               dpy, window, fgc, f);
783                     }
784                 }
785             }
786         }
787         else /* f->draw_centers */
788         {
789             XDrawPoint(dpy, window, fgc, c1->x, c1->y);
790         }
791     }
792 }
793
794 struct state {
795   Display *dpy;
796   Window window;
797
798   unsigned int max_cycles;
799   int growth_delay;
800   GC fgc;
801   XGCValues gcv;
802   XWindowAttributes xgwa;
803
804   struct field *f;
805 };
806
807
808 static void *
809 interaggregate_init (Display *dpy, Window window)
810 {
811     struct state *st = (struct state *) calloc (1, sizeof(*st));
812
813 #ifdef TIME_ME
814     int frames;
815     struct timeval tm1, tm2;
816     double tdiff;
817 #endif
818
819     st->dpy = dpy;
820     st->window = window;
821     st->f = init_field();
822     st->growth_delay = (get_integer_resource(st->dpy, "growthDelay", "Integer"));
823     st->max_cycles = (get_integer_resource(st->dpy, "maxCycles", "Integer"));
824     st->f->num_circles = (get_integer_resource(st->dpy, "numCircles", "Integer"));
825     st->f->percent_orbits = (get_integer_resource(st->dpy, "percentOrbits", "Integer"));
826     st->f->base_orbits = (get_integer_resource(st->dpy, "baseOrbits", "Integer"));
827     st->f->base_on_center = (get_boolean_resource(st->dpy, "baseOnCenter", "Boolean"));
828     st->f->draw_centers = (get_boolean_resource(st->dpy, "drawCenters", "Boolean"));
829
830     if (st->f->num_circles <= 1) 
831     {
832         fprintf(stderr, "%s: Minimum number of circles is 2\n", 
833                 progname);
834         exit (1);
835     }
836
837     if ( (st->f->percent_orbits < 0) || (st->f->percent_orbits > 100) )
838     {
839         fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n", 
840                 progname);
841         exit (1);
842     }
843
844     if ( (st->f->base_orbits < 0) || (st->f->base_orbits > 100) )
845     {
846         fprintf(stderr, "%s: base-oribts must be between 0 and 100\n", 
847                 progname);
848         exit (1);
849     }
850
851     if ( st->f->percent_orbits == 100 )
852         st->f->base_on_center = True;
853
854     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
855
856     build_colors(st->f, st->dpy, &st->xgwa);
857
858     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
859                                         "foreground", "Foreground");
860     st->gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
861                                         "background", "Background");
862
863     st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
864
865     st->f->height = st->xgwa.height;
866     st->f->width = st->xgwa.width;
867     st->f->visdepth = st->xgwa.depth;
868     st->f->fgcolor = st->gcv.foreground;
869     st->f->bgcolor = st->gcv.background;
870
871     /* Initialize stuff */
872     build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
873
874 #ifdef TIME_ME
875     gettimeofday(&tm1, NULL);
876     frames = 0;
877 #endif
878
879     return st;
880 }
881
882
883 static unsigned long
884 interaggregate_draw (Display *dpy, Window window, void *closure)
885 {
886   struct state *st = (struct state *) closure;
887
888   if ((st->f->cycles % 10) == 0) 
889     {
890       /* Restart if the window size changes */
891       XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
892
893       if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) 
894         {
895           st->f->height = st->xgwa.height;
896           st->f->width = st->xgwa.width;
897           st->f->visdepth = st->xgwa.depth;
898
899           build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
900           XSetForeground(st->dpy, st->fgc, st->gcv.background);
901           XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
902           XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
903         }
904     }
905
906   moveCircles(st->f);
907   drawIntersections(st->dpy, st->window, st->fgc, st->f);
908
909   st->f->cycles++;
910
911
912   if (st->f->cycles >= st->max_cycles && st->max_cycles != 0)
913     {
914       build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
915       XSetForeground(st->dpy, st->fgc, st->gcv.background);
916       XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
917       XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
918     }
919
920 #ifdef TIME_ME
921   frames++;
922   gettimeofday(&tm2, NULL);
923
924   tdiff = (tm2.tv_sec - tm1.tv_sec) 
925     + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
926
927   if ( tdiff > 1 )
928     {
929       fprintf(stderr, "fps: %d %f %f\n", 
930               frames, tdiff, frames / tdiff );
931
932       fprintf(stderr, "intersections: %d %d %f\n", 
933               f->intersection_count, f->possible_intersections, 
934               ((double)f->intersection_count) / 
935               f->possible_intersections);
936
937       fprintf(stderr, "fpi: %f\n", 
938               ((double)frames) / f->intersection_count );
939
940       frames = 0;
941       tm1.tv_sec = tm2.tv_sec;
942       tm1.tv_usec = tm2.tv_usec;
943
944       f->intersection_count = f->possible_intersections = 0;
945     }
946 #endif
947
948   return st->growth_delay;
949 }
950
951
952 static void
953 interaggregate_reshape (Display *dpy, Window window, void *closure, 
954                  unsigned int w, unsigned int h)
955 {
956 }
957
958 static Bool
959 interaggregate_event (Display *dpy, Window window, void *closure, XEvent *event)
960 {
961   return False;
962 }
963
964 static void
965 interaggregate_free (Display *dpy, Window window, void *closure)
966 {
967   struct state *st = (struct state *) closure;
968   free (st);
969 }
970
971
972 XSCREENSAVER_MODULE ("Interaggregate", interaggregate)