http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[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     case 24:
294 #ifdef HAVE_COCOA
295         /* This program idiotically does not go through a color map, so
296            we have to hardcode in knowledge of how jwxyz.a packs pixels!
297            Fix it to go through st->colors[st->ncolors] instead!
298          */
299         ret = 0xFF000000 | (r << 16) | (g << 8) | b;
300 #else
301         ret |= (r << 16) | (g << 8) | b;
302 #endif
303         break;
304     case 16:
305         ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
306         break;
307     case 15:
308         ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
309         break;
310     }
311
312     return ret;
313 }
314
315 /* alpha blended point drawing -- this is Not Right and will likely fail on 
316  * non-intel platforms as it is now, needs fixing */
317 static inline unsigned long trans_point(int x1, int y1, unsigned long myc, double a, 
318                                  struct field *f) 
319 {
320     if (a >= 1.0) 
321     {
322         ref_pixel(f, x1, y1) = myc;
323         return myc;
324     } 
325     else 
326     {
327         int or=0, og=0, ob=0;
328         int r=0, g=0, b=0;
329         int nr, ng, nb;
330         unsigned long c;
331
332         c = ref_pixel(f, x1, y1);
333
334         point2rgb(f->visdepth, c, &or, &og, &ob);
335         point2rgb(f->visdepth, myc, &r, &g, &b);
336
337         nr = or + (r - or) * a;
338         ng = og + (g - og) * a;
339         nb = ob + (b - ob) * a;
340
341         c = rgb2point(f->visdepth, nr, ng, nb);
342
343         ref_pixel(f, x1, y1) = c;
344
345         return c;
346     }
347 }
348
349 static inline void drawPoint(int x, int y, unsigned long color, double intensity,
350                       Display *dpy, Window window, GC fgc, struct field *f)
351                
352 {
353     unsigned long c;
354
355     while ( x >= f->width ) x -= f->width;
356     while ( x < 0 ) x += f->width;
357         
358     while ( y >= f->height ) y -= f->height;
359     while ( y < 0 ) y += f->height;
360
361     /* if ( in_bounds(f, x, y) ) ... */
362
363     c = trans_point(x, y, color, intensity, f);
364
365     XSetForeground(dpy, fgc, c);
366     XDrawPoint(dpy, window, fgc, x, y);
367 }
368
369 static inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
370                   Display *dpy, Window window, GC fgc, 
371                   struct field *f)
372 {
373     /* the sand painter */
374
375     double inc, sandp;
376     int i;
377
378     /* XXX try adding tpoint here, like orig */
379
380     /* jitter the painter's values */
381     painter->gain += frand(0.05) - 0.025;
382     
383     if ( painter->gain > f->max_gain )
384         painter->gain = -f->max_gain;
385     else if ( painter->gain < -f->max_gain )
386         painter->gain = f->max_gain;
387
388     painter->p += frand(0.1) - 0.05;
389     
390     if ( 0 < painter->p )
391         painter->p = 0;
392     else if ( painter->p > 1.0 )
393         painter->p = 1.0;
394
395     /* replace 0.1 with 1 / f->grains */
396     inc = painter->gain * 0.1;
397     sandp = 0;
398
399     for(i = 0; i <= 10; ++i)
400     {
401         int drawx, drawy;
402         double sp, sm;
403         double intensity = 0.1 - 0.009 * i;
404
405         sp = sin(painter->p + sandp);
406         drawx = ax + (bx - ax) * sp;
407         drawy = ay + (by - ay) * sp;
408
409         drawPoint(drawx, drawy, painter->color,
410                   intensity,
411                   dpy, window, fgc, f);
412
413         sm = sin(painter->p - sandp);
414         drawx = ax + (bx - ax) * sm;
415         drawy = ay + (by - ay) * sm;
416
417         drawPoint(drawx, drawy, painter->color,
418                   intensity,
419                   dpy, window, fgc, f);
420
421         sandp += inc;
422     }
423 }
424
425 static void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa) 
426 {
427
428     XColor tmpcolor;
429     int i;
430     /* Count the colors in our map and assign them in a horrifically inefficient 
431      * manner but it only happens once */
432
433     for( f->numcolors = 0; 
434          rgb_colormap[f->numcolors] != NULL; 
435          ++f->numcolors )
436     {
437         ;
438     }
439
440     f->parsedcolors = (unsigned long *) calloc(f->numcolors,
441                                                sizeof(unsigned long));
442     if ( f->parsedcolors == NULL )
443     {
444         fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
445                 progname);
446         exit(1);
447     }
448         
449     for(i = 0; i < f->numcolors; ++i)
450     {
451         if (!XParseColor(dpy, xgwa->colormap, 
452                          rgb_colormap[i], &tmpcolor)) 
453         {
454             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
455                     rgb_colormap[i]);
456             exit(1);
457         }
458
459         if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor)) 
460         {
461             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
462                     rgb_colormap[i]);
463             exit(1);
464         }
465
466         f->parsedcolors[i] = tmpcolor.pixel;
467
468     }
469 }
470
471 /* used when the window is resized */
472 static void build_img(struct field *f)
473 {
474     if (f->off_img) {
475         free(f->off_img);
476         f->off_img = NULL;
477     }
478
479     f->off_img = (unsigned long *) calloc(f->width * f->height,
480                                           sizeof(unsigned long));
481                                            
482
483     if ( f->off_img == NULL )
484     {
485         fprintf(stderr, "%s: Failed to allocate off_img\n",
486                 progname);
487         exit(1);
488     }
489
490     memset(f->off_img, f->bgcolor, 
491            sizeof(unsigned long) * f->width * f->height);
492 }
493
494 static void free_circles(struct field *f) 
495 {
496     int i;
497
498     if ( f->circles != NULL )
499     {
500         for(i = 0; i < f->num_circles; ++i)
501         {
502             free (f->circles[i].painters);
503         }
504
505         free (f->circles);
506         f->circles = NULL;
507     }
508 }
509
510 static void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
511                  struct field *f) 
512 {
513     int i;
514     int num_orbits;
515     int base_orbits;
516     int orbit_start;
517     build_img(f);
518
519     f->center_of_universe.x = f->width / 2.0;
520     f->center_of_universe.y = f->height / 2.0;
521     f->center_of_universe.r = MAX(f->width, f->height) / 2.0;
522
523     num_orbits = (f->percent_orbits * f->num_circles) / 100;
524     orbit_start = f->num_circles - num_orbits;
525     base_orbits = orbit_start + (num_orbits * f->base_orbits) / 100;
526
527     free_circles(f);
528
529     f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
530     if ( f->circles == NULL )
531     {
532         fprintf(stderr, "%s: Failed to allocate off_img\n",
533                 progname);
534         exit(1);
535     }
536
537     for(i = 0; i < f->num_circles; ++i)
538     {
539         int j;
540         Circle *circle = f->circles + i;
541
542         /* make this a pref */
543
544         if ( i >= orbit_start )
545           circle->path_type = ORBIT;
546         else
547           circle->path_type = LINEAR;
548
549
550         if ( circle->path_type == LINEAR )
551         {
552             circle->x = frand(f->width);
553             circle->y = frand(f->height);
554
555             circle->dx = frand(0.5) - 0.25; 
556             circle->dy = frand(0.5) - 0.25;
557             /* circle->dy = 0; */
558             /* circle->r  = f->height * (0.05 + frand(0.1)); */
559             circle->radius = 5 + frand(55);
560
561             /* in case we want orbits based on lines */
562             circle->r = MIN(f->width, f->height) / 2.0;
563             circle->center = NULL;
564         }
565         else /* == ORBIT */
566         {
567             if (i < base_orbits )
568             {
569                 if ( f->base_on_center )
570                     circle->center = &f->center_of_universe; 
571                 else
572                 {
573                     circle->center = f->circles + 
574                         ((int)frand(orbit_start - 0.1));
575                 }
576
577                 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
578
579                 /* circle->radius = 5 + frand(55); */
580             }
581             else
582             {
583                 /* give a preference for the earlier circles */
584
585                 double p = frand(0.9);
586
587                 circle->center = f->circles + (int) (p*i);
588
589                 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r); 
590                 /* circle->r = 1 + frand(circle->center->r / 2); */
591
592
593                 /* circle->radius = MAX(5, frand(circle->r)); */
594                 /* circle->radius = 5 + frand(55); */
595             }
596
597             circle->radius = 5 + frand(MIN(55, circle->r)); 
598             circle->dtheta = (frand(0.5) - 0.25) / circle->r;
599             circle->theta = frand(2 * M_PI);
600
601             circle->x = circle->r * cos(circle->theta) + circle->center->x;
602             circle->y = circle->r * sin(circle->theta) + circle->center->y;
603
604         }
605
606         /* make this a command line option */
607         circle->num_painters = 3;
608         circle->painters = (SandPainter*) calloc(circle->num_painters, 
609                                                  sizeof(SandPainter));
610         if ( circle->painters == NULL )
611         {
612             fprintf(stderr, "%s: failed to allocate painters", progname);
613             exit(1);
614         }
615
616         for(j = 0; j < circle->num_painters; ++j)
617         {
618             SandPainter *painter = circle->painters + j;
619
620             painter->gain = frand(0.09) + 0.01;
621             painter->p = frand(1.0);
622             painter->color = 
623                 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
624         }
625     }
626 }
627
628 static void moveCircles(struct field *f)
629 {
630     int i;
631
632     for(i = 0; i < f->num_circles; ++i)
633     {
634         Circle *circle = f->circles + i;
635
636         if ( circle->path_type == LINEAR )
637         {
638             circle->x += circle->dx;
639             circle->y += circle->dy;
640
641 #if 0
642             if ( circle->x < -circle->radius )
643                 circle->x = f->width + circle->radius;
644             else if ( circle->x >= f->width + circle->radius )
645                 circle->x = -circle->radius;
646
647             if ( circle->y < -circle->radius )
648                 circle->y = f->height + circle->radius;
649             else if ( circle->y >= f->height + circle->radius )
650                 circle->y = -circle->radius;
651 #else
652             if ( circle->x < 0 ) circle->x += f->width;
653             else if ( circle->x >= f->width ) circle->x -= f->width;
654
655             if ( circle->y < 0 ) circle->y += f->height;
656             else if ( circle->y >= f->height ) circle->y -= f->height;
657 #endif
658         }
659         else /* (circle->path_type == ORBIT) */
660         {
661             circle->theta += circle->dtheta;
662
663             if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
664             else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
665
666             circle->x = circle->r * cos(circle->theta) + circle->center->x;
667             circle->y = circle->r * sin(circle->theta) + circle->center->y;
668
669 #if 0
670             if ( circle->x < -circle->radius )
671                 circle->x += f->width + 2 * circle->radius;
672             else if ( circle->x >= f->width + circle->radius )
673                 circle->x -= f->width + 2 * circle->radius;
674
675             if ( circle->y < -circle->radius )
676                 circle->y += f->height + 2 * circle->radius;
677             else if ( circle->y >= f->height + circle->radius )
678                 circle->y -= f->height + 2 * circle->radius;
679 #else
680             if ( circle->x < 0 ) circle->x += f->width;
681             else if ( circle->x >= f->width ) circle->x -= f->width;
682
683             if ( circle->y < 0 ) circle->y += f->height;
684             else if ( circle->y >= f->height ) circle->y -= f->height;
685 #endif
686         }
687     }
688 }
689
690 static void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
691 {
692     int i,j;
693
694     /* One might be tempted to think 'hey, this is a crude algorithm
695      * that is going to check each of the n (n-1) / 2 possible
696      * intersections!  Why not try bsp trees, quad trees, etc, etc,
697      * etc'
698      *
699      * In practice the time spent drawing the intersection of two
700      * circles dwarfs the time takes to check for intersection.
701      * Profiling on a 640x480 screen with 100 circles shows possible
702      * speed gains to be only a couple of percent.
703      * 
704      * But hey, if you're bored, go have fun.  Let me know how it
705      * turns out.
706      */
707
708
709     for(i = 0; i < f->num_circles; ++i)
710     {
711         Circle *c1 = f->circles + i;
712
713         if ( !f->draw_centers )
714         {
715             /* the default branch */
716
717             for(j = i + 1; j < f->num_circles; ++j)
718             {
719                 double d, dsqr, dx, dy;
720                 Circle *c2 = f->circles + j;
721
722 #ifdef TIME_ME
723                 ++f->possible_intersections;
724 #endif
725                 dx = c2->x - c1->x;
726                 dy = c2->y - c1->y;
727
728                 dsqr = dx * dx + dy * dy;
729                 d = sqrt(dsqr);
730
731                 if ( (fabs(dx) < (c1->radius + c2->radius)) &&
732                      (fabs(dy) < (c1->radius + c2->radius)) &&
733                      ( d < (c1->radius + c2->radius) ) &&
734                      ( d > fabs(c1->radius - c2->radius) ) )
735                 {
736                     double d1, d2, r1sqr; 
737                     double bx, by;
738                     double midpx, midpy;
739                     double int1x, int1y;
740                     double int2x, int2y;
741                     int s;
742
743                     /* woo-hoo.  the circles are neither outside nor
744                      * inside each other.  they intersect.  
745                      *
746                      * Now, compute the coordinates of the points of
747                      * intersection 
748                      */
749
750 #ifdef TIME_ME
751                     ++f->intersection_count;
752 #endif
753
754                     /* unit vector in direction of c1 to c2 */
755                     bx = dx / d;
756                     by = dy / d;
757
758                     r1sqr = c1->radius * c1->radius;
759
760                     /* distance from c1's center midpoint of intersection
761                      * points */
762
763                     d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
764                 
765                     midpx = c1->x + d1 * bx;
766                     midpy = c1->y + d1 * by;
767
768                     /* distance from midpoint to points of intersection */
769
770                     d2 = sqrt(r1sqr - d1 * d1);
771
772                     int1x = midpx + d2 * by;
773                     int1y = midpy - d2 * bx;
774
775                     int2x = midpx - d2 * by;
776                     int2y = midpy + d2 * bx;
777
778                     for(s = 0; s < c1->num_painters; ++s)
779                     {
780                         paint(c1->painters + s, int1x, int1y, int2x, int2y, 
781                               dpy, window, fgc, f);
782                     }
783                 }
784             }
785         }
786         else /* f->draw_centers */
787         {
788             XDrawPoint(dpy, window, fgc, c1->x, c1->y);
789         }
790     }
791 }
792
793 struct state {
794   Display *dpy;
795   Window window;
796
797   unsigned int max_cycles;
798   int growth_delay;
799   GC fgc;
800   XGCValues gcv;
801   XWindowAttributes xgwa;
802
803   struct field *f;
804 };
805
806
807 static void *
808 interaggregate_init (Display *dpy, Window window)
809 {
810     struct state *st = (struct state *) calloc (1, sizeof(*st));
811
812 #ifdef TIME_ME
813     int frames;
814     struct timeval tm1, tm2;
815     double tdiff;
816 #endif
817
818     st->dpy = dpy;
819     st->window = window;
820     st->f = init_field();
821     st->growth_delay = (get_integer_resource(st->dpy, "growthDelay", "Integer"));
822     st->max_cycles = (get_integer_resource(st->dpy, "maxCycles", "Integer"));
823     st->f->num_circles = (get_integer_resource(st->dpy, "numCircles", "Integer"));
824     st->f->percent_orbits = (get_integer_resource(st->dpy, "percentOrbits", "Integer"));
825     st->f->base_orbits = (get_integer_resource(st->dpy, "baseOrbits", "Integer"));
826     st->f->base_on_center = (get_boolean_resource(st->dpy, "baseOnCenter", "Boolean"));
827     st->f->draw_centers = (get_boolean_resource(st->dpy, "drawCenters", "Boolean"));
828
829     if (st->f->num_circles <= 1) 
830     {
831         fprintf(stderr, "%s: Minimum number of circles is 2\n", 
832                 progname);
833         exit (1);
834     }
835
836     if ( (st->f->percent_orbits < 0) || (st->f->percent_orbits > 100) )
837     {
838         fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n", 
839                 progname);
840         exit (1);
841     }
842
843     if ( (st->f->base_orbits < 0) || (st->f->base_orbits > 100) )
844     {
845         fprintf(stderr, "%s: base-oribts must be between 0 and 100\n", 
846                 progname);
847         exit (1);
848     }
849
850     if ( st->f->percent_orbits == 100 )
851         st->f->base_on_center = True;
852
853     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
854
855     build_colors(st->f, st->dpy, &st->xgwa);
856
857     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
858                                         "foreground", "Foreground");
859     st->gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
860                                         "background", "Background");
861
862     st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
863
864     st->f->height = st->xgwa.height;
865     st->f->width = st->xgwa.width;
866     st->f->visdepth = st->xgwa.depth;
867     st->f->fgcolor = st->gcv.foreground;
868     st->f->bgcolor = st->gcv.background;
869
870     /* Initialize stuff */
871     build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
872
873 #ifdef TIME_ME
874     gettimeofday(&tm1, NULL);
875     frames = 0;
876 #endif
877
878     return st;
879 }
880
881
882 static unsigned long
883 interaggregate_draw (Display *dpy, Window window, void *closure)
884 {
885   struct state *st = (struct state *) closure;
886
887   if ((st->f->cycles % 10) == 0) 
888     {
889       /* Restart if the window size changes */
890       XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
891
892       if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) 
893         {
894           st->f->height = st->xgwa.height;
895           st->f->width = st->xgwa.width;
896           st->f->visdepth = st->xgwa.depth;
897
898           build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
899           XSetForeground(st->dpy, st->fgc, st->gcv.background);
900           XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
901           XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
902         }
903     }
904
905   moveCircles(st->f);
906   drawIntersections(st->dpy, st->window, st->fgc, st->f);
907
908   st->f->cycles++;
909
910
911   if (st->f->cycles >= st->max_cycles && st->max_cycles != 0)
912     {
913       build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
914       XSetForeground(st->dpy, st->fgc, st->gcv.background);
915       XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
916       XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
917     }
918
919 #ifdef TIME_ME
920   frames++;
921   gettimeofday(&tm2, NULL);
922
923   tdiff = (tm2.tv_sec - tm1.tv_sec) 
924     + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
925
926   if ( tdiff > 1 )
927     {
928       fprintf(stderr, "fps: %d %f %f\n", 
929               frames, tdiff, frames / tdiff );
930
931       fprintf(stderr, "intersections: %d %d %f\n", 
932               f->intersection_count, f->possible_intersections, 
933               ((double)f->intersection_count) / 
934               f->possible_intersections);
935
936       fprintf(stderr, "fpi: %f\n", 
937               ((double)frames) / f->intersection_count );
938
939       frames = 0;
940       tm1.tv_sec = tm2.tv_sec;
941       tm1.tv_usec = tm2.tv_usec;
942
943       f->intersection_count = f->possible_intersections = 0;
944     }
945 #endif
946
947   return st->growth_delay;
948 }
949
950
951 static void
952 interaggregate_reshape (Display *dpy, Window window, void *closure, 
953                  unsigned int w, unsigned int h)
954 {
955 }
956
957 static Bool
958 interaggregate_event (Display *dpy, Window window, void *closure, XEvent *event)
959 {
960   return False;
961 }
962
963 static void
964 interaggregate_free (Display *dpy, Window window, void *closure)
965 {
966   struct state *st = (struct state *) closure;
967   free (st);
968 }
969
970
971 XSCREENSAVER_MODULE ("Interaggregate", interaggregate)