From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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 #ifdef HAVE_MOBILE
89     "*ignoreRotation: True",
90 #endif
91     0
92 };
93
94 static XrmOptionDescRec interaggregate_options[] = 
95 {
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"}, 
105     {0, 0, 0, 0}
106 };
107
108 /* Raw colormap extracted from pollockEFF.gif */
109 #if 0
110 char *rgb_colormap[] = 
111 {
112     "#FFFFFF", /* white */
113     "#000000", /* black */
114     "#000000", /* more black */
115     /* "#736451",  */
116     "#4e3e2e", /* olive */
117     /* "#666666", */
118     "#694d35",  /* camel */
119     "#b9a88c",  /* tan */
120     0
121 };
122 #endif
123
124 static const char *rgb_colormap[] = 
125 {
126     "#FFFFFF", /* white */
127     "#000000", /* more black */
128     "#000000", /* more black */
129     "#4e3e2e", /* olive */
130     "#694d35",  /* camel */
131     "#b0a085",  /* tan */
132     "#e6d3ae",
133     0
134 };
135
136 /* black white brown olive grey camel */
137
138 typedef enum { LINEAR, ORBIT } PathType;
139
140 typedef struct 
141 {
142
143     unsigned long color;
144     double gain;
145     double p;
146
147 } SandPainter;
148
149 typedef struct _circle
150 {
151     double radius;
152
153     double x;
154     double y;
155
156     PathType path_type;
157
158     /* for a linear path */
159     double dx;
160     double dy;
161
162     /* for orbital path */
163     double theta;
164     double r; 
165     double dtheta;
166     
167     struct _circle* center;
168
169     int num_painters;
170     SandPainter* painters;
171
172 } Circle;
173
174
175 struct field 
176 {
177     int height;
178     int width;
179
180     int num_circles;
181     Circle* circles;
182
183     int percent_orbits;
184     int base_orbits;
185     Bool base_on_center;
186
187     /* used for orbits circling the center of the screen */
188     Circle center_of_universe;
189
190     /* Raw map of pixels we need to keep for alpha blending */
191     unsigned long int *off_img;
192    
193     /* color parms */
194     int numcolors;
195     unsigned long *parsedcolors;
196     unsigned long fgcolor;
197     unsigned long bgcolor;
198     int visdepth;
199
200     unsigned int cycles;
201
202     double max_gain;
203
204     /* for debugging */
205     Bool draw_centers;
206
207     /* for profiling whatnot */ 
208     int possible_intersections;
209     int intersection_count;
210 };
211
212
213 static struct field *
214 init_field(void)
215 {
216     struct field *f = (struct field*) malloc(sizeof(struct field));
217     if ( f == NULL )
218     {
219         fprintf(stderr, "%s: Failed to allocate field!\n", progname);
220         exit(1);
221     }
222
223     f->height = 0;
224     f->width = 0;
225     f->num_circles = 0;
226     f->circles = NULL;
227     f->percent_orbits = 0;
228     f->base_orbits = 0;
229     f->base_on_center = False;
230     f->off_img = NULL;
231     f->numcolors = 0;
232     f->parsedcolors = NULL;
233     f->fgcolor = 0;
234     f->bgcolor = 0;
235     f->visdepth = 0;
236
237     f->cycles = 0;
238
239     f->max_gain = 0.22;
240
241     f->draw_centers = False;
242
243     f->possible_intersections = 0;
244     f->intersection_count = 0;
245
246     return f;
247 }
248
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)])
251
252 #define in_bounds(f, x, y) ((x >= 0) && (x < f->width) && (y >= 0) && (y < f->height))
253
254 /* Consider rewriting with XQueryColor, or ImageByteOrder */
255
256 static inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) 
257 {
258     switch(depth) 
259     {
260     case 32:
261     case 24:
262 #ifdef HAVE_JWXYZ
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!
266          */
267         *r = (c & 0x00ff0000) >> 16; 
268         *g = (c & 0x0000ffff) >>  8;
269         *b = (c & 0x000000ff);
270 #else
271         *b = c & 0xff; 
272         *g = (c & 0xff00) >> 8; 
273         *r = (c & 0xff0000) >> 16; 
274 #endif
275         break;
276     case 16:
277         *b = (int) (c & 0x1f) << 3;
278         *g = (int) ((c >> 5) & 0x3f) << 2;
279         *r = (int) ((c >> 11) & 0x1f) << 3;
280         break;
281     case 15:
282         *b = (int) (c & 0x1f) << 3;
283         *g = (int) ((c >> 5) & 0x1f) << 3;
284         *r = (int) ((c >> 10) & 0x1f) << 3;
285         break;
286     }
287 }
288
289 static inline unsigned long rgb2point(int depth, int r, int g, int b) 
290 {
291     unsigned long ret = 0;
292
293     switch(depth) 
294     {
295     case 32:
296     case 24:
297 #ifdef HAVE_JWXYZ
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!
301          */
302         ret = 0xFF000000 | (r << 16) | (g << 8) | b;
303 #else
304         ret |= (r << 16) | (g << 8) | b;
305 #endif
306         break;
307     case 16:
308         ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
309         break;
310     case 15:
311         ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
312         break;
313     }
314
315     return ret;
316 }
317
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, 
321                                  struct field *f) 
322 {
323     if (a >= 1.0) 
324     {
325         ref_pixel(f, x1, y1) = myc;
326         return myc;
327     } 
328     else 
329     {
330         int or=0, og=0, ob=0;
331         int r=0, g=0, b=0;
332         int nr, ng, nb;
333         unsigned long c;
334
335         c = ref_pixel(f, x1, y1);
336
337         point2rgb(f->visdepth, c, &or, &og, &ob);
338         point2rgb(f->visdepth, myc, &r, &g, &b);
339
340         nr = or + (r - or) * a;
341         ng = og + (g - og) * a;
342         nb = ob + (b - ob) * a;
343
344         c = rgb2point(f->visdepth, nr, ng, nb);
345
346         ref_pixel(f, x1, y1) = c;
347
348         return c;
349     }
350 }
351
352 static inline void drawPoint(int x, int y, unsigned long color, double intensity,
353                       Display *dpy, Window window, GC fgc, struct field *f)
354                
355 {
356     unsigned long c;
357
358     while ( x >= f->width ) x -= f->width;
359     while ( x < 0 ) x += f->width;
360         
361     while ( y >= f->height ) y -= f->height;
362     while ( y < 0 ) y += f->height;
363
364     /* if ( in_bounds(f, x, y) ) ... */
365
366     c = trans_point(x, y, color, intensity, f);
367
368     XSetForeground(dpy, fgc, c);
369     XDrawPoint(dpy, window, fgc, x, y);
370 }
371
372 static inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
373                   Display *dpy, Window window, GC fgc, 
374                   struct field *f)
375 {
376     /* the sand painter */
377
378     double inc, sandp;
379     int i;
380
381     /* XXX try adding tpoint here, like orig */
382
383     /* jitter the painter's values */
384     painter->gain += frand(0.05) - 0.025;
385     
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;
390
391     painter->p += frand(0.1) - 0.05;
392     
393     if ( 0 < painter->p )
394         painter->p = 0;
395     else if ( painter->p > 1.0 )
396         painter->p = 1.0;
397
398     /* replace 0.1 with 1 / f->grains */
399     inc = painter->gain * 0.1;
400     sandp = 0;
401
402     for(i = 0; i <= 10; ++i)
403     {
404         int drawx, drawy;
405         double sp, sm;
406         double intensity = 0.1 - 0.009 * i;
407
408         sp = sin(painter->p + sandp);
409         drawx = ax + (bx - ax) * sp;
410         drawy = ay + (by - ay) * sp;
411
412         drawPoint(drawx, drawy, painter->color,
413                   intensity,
414                   dpy, window, fgc, f);
415
416         sm = sin(painter->p - sandp);
417         drawx = ax + (bx - ax) * sm;
418         drawy = ay + (by - ay) * sm;
419
420         drawPoint(drawx, drawy, painter->color,
421                   intensity,
422                   dpy, window, fgc, f);
423
424         sandp += inc;
425     }
426 }
427
428 static void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa) 
429 {
430
431     XColor tmpcolor;
432     int i;
433     /* Count the colors in our map and assign them in a horrifically inefficient 
434      * manner but it only happens once */
435
436     for( f->numcolors = 0; 
437          rgb_colormap[f->numcolors] != NULL; 
438          ++f->numcolors )
439     {
440         ;
441     }
442
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 )
447     {
448         fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
449                 progname);
450         exit(1);
451     }
452         
453     for(i = 0; i < f->numcolors; ++i)
454     {
455         if (!XParseColor(dpy, xgwa->colormap, 
456                          rgb_colormap[i], &tmpcolor)) 
457         {
458             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
459                     rgb_colormap[i]);
460             exit(1);
461         }
462
463         if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor)) 
464         {
465             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
466                     rgb_colormap[i]);
467             exit(1);
468         }
469
470         f->parsedcolors[i] = tmpcolor.pixel;
471
472     }
473 }
474
475 /* used when the window is resized */
476 static void build_img(struct field *f)
477 {
478     if (f->off_img) {
479         free(f->off_img);
480         f->off_img = NULL;
481     }
482
483     f->off_img = (unsigned long *) calloc(f->width * f->height,
484                                           sizeof(unsigned long));
485                                            
486
487     if ( f->off_img == NULL )
488     {
489         fprintf(stderr, "%s: Failed to allocate off_img\n",
490                 progname);
491         exit(1);
492     }
493
494     memset(f->off_img, f->bgcolor, 
495            sizeof(unsigned long) * f->width * f->height);
496 }
497
498 static void free_circles(struct field *f) 
499 {
500     int i;
501
502     if ( f->circles != NULL )
503     {
504         for(i = 0; i < f->num_circles; ++i)
505         {
506             free (f->circles[i].painters);
507         }
508
509         free (f->circles);
510         f->circles = NULL;
511     }
512 }
513
514 static void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
515                  struct field *f) 
516 {
517     int i;
518     int num_orbits;
519     int base_orbits;
520     int orbit_start;
521     build_img(f);
522
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;
526
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;
530
531     free_circles(f);
532
533     f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
534     if ( f->circles == NULL )
535     {
536         fprintf(stderr, "%s: Failed to allocate off_img\n",
537                 progname);
538         exit(1);
539     }
540
541     for(i = 0; i < f->num_circles; ++i)
542     {
543         int j;
544         Circle *circle = f->circles + i;
545
546         /* make this a pref */
547
548         if ( i >= orbit_start )
549           circle->path_type = ORBIT;
550         else
551           circle->path_type = LINEAR;
552
553
554         if ( circle->path_type == LINEAR )
555         {
556             circle->x = frand(f->width);
557             circle->y = frand(f->height);
558
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);
564
565             /* in case we want orbits based on lines */
566             circle->r = MIN(f->width, f->height) / 2.0;
567             circle->center = NULL;
568         }
569         else /* == ORBIT */
570         {
571             if (i < base_orbits )
572             {
573                 if ( f->base_on_center )
574                     circle->center = &f->center_of_universe; 
575                 else
576                 {
577                     circle->center = f->circles + 
578                         ((int)frand(orbit_start - 0.1));
579                 }
580
581                 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
582
583                 /* circle->radius = 5 + frand(55); */
584             }
585             else
586             {
587                 /* give a preference for the earlier circles */
588
589                 double p = frand(0.9);
590
591                 circle->center = f->circles + (int) (p*i);
592
593                 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r); 
594                 /* circle->r = 1 + frand(circle->center->r / 2); */
595
596
597                 /* circle->radius = MAX(5, frand(circle->r)); */
598                 /* circle->radius = 5 + frand(55); */
599             }
600
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);
604
605             circle->x = circle->r * cos(circle->theta) + circle->center->x;
606             circle->y = circle->r * sin(circle->theta) + circle->center->y;
607
608         }
609
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 )
615         {
616             fprintf(stderr, "%s: failed to allocate painters", progname);
617             exit(1);
618         }
619
620         for(j = 0; j < circle->num_painters; ++j)
621         {
622             SandPainter *painter = circle->painters + j;
623
624             painter->gain = frand(0.09) + 0.01;
625             painter->p = frand(1.0);
626             painter->color = 
627                 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
628         }
629     }
630 }
631
632 static void moveCircles(struct field *f)
633 {
634     int i;
635
636     for(i = 0; i < f->num_circles; ++i)
637     {
638         Circle *circle = f->circles + i;
639
640         if ( circle->path_type == LINEAR )
641         {
642             circle->x += circle->dx;
643             circle->y += circle->dy;
644
645 #if 0
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;
650
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;
655 #else
656             if ( circle->x < 0 ) circle->x += f->width;
657             else if ( circle->x >= f->width ) circle->x -= f->width;
658
659             if ( circle->y < 0 ) circle->y += f->height;
660             else if ( circle->y >= f->height ) circle->y -= f->height;
661 #endif
662         }
663         else /* (circle->path_type == ORBIT) */
664         {
665             circle->theta += circle->dtheta;
666
667             if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
668             else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
669
670             circle->x = circle->r * cos(circle->theta) + circle->center->x;
671             circle->y = circle->r * sin(circle->theta) + circle->center->y;
672
673 #if 0
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;
678
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;
683 #else
684             if ( circle->x < 0 ) circle->x += f->width;
685             else if ( circle->x >= f->width ) circle->x -= f->width;
686
687             if ( circle->y < 0 ) circle->y += f->height;
688             else if ( circle->y >= f->height ) circle->y -= f->height;
689 #endif
690         }
691     }
692 }
693
694 static void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
695 {
696     int i,j;
697
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,
701      * etc'
702      *
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.
707      * 
708      * But hey, if you're bored, go have fun.  Let me know how it
709      * turns out.
710      */
711
712
713     for(i = 0; i < f->num_circles; ++i)
714     {
715         Circle *c1 = f->circles + i;
716
717         if ( !f->draw_centers )
718         {
719             /* the default branch */
720
721             for(j = i + 1; j < f->num_circles; ++j)
722             {
723                 double d, dsqr, dx, dy;
724                 Circle *c2 = f->circles + j;
725
726 #ifdef TIME_ME
727                 ++f->possible_intersections;
728 #endif
729                 dx = c2->x - c1->x;
730                 dy = c2->y - c1->y;
731
732                 dsqr = dx * dx + dy * dy;
733                 d = sqrt(dsqr);
734
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) ) )
739                 {
740                     double d1, d2, r1sqr; 
741                     double bx, by;
742                     double midpx, midpy;
743                     double int1x, int1y;
744                     double int2x, int2y;
745                     int s;
746
747                     /* woo-hoo.  the circles are neither outside nor
748                      * inside each other.  they intersect.  
749                      *
750                      * Now, compute the coordinates of the points of
751                      * intersection 
752                      */
753
754 #ifdef TIME_ME
755                     ++f->intersection_count;
756 #endif
757
758                     /* unit vector in direction of c1 to c2 */
759                     bx = dx / d;
760                     by = dy / d;
761
762                     r1sqr = c1->radius * c1->radius;
763
764                     /* distance from c1's center midpoint of intersection
765                      * points */
766
767                     d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
768                 
769                     midpx = c1->x + d1 * bx;
770                     midpy = c1->y + d1 * by;
771
772                     /* distance from midpoint to points of intersection */
773
774                     d2 = sqrt(r1sqr - d1 * d1);
775
776                     int1x = midpx + d2 * by;
777                     int1y = midpy - d2 * bx;
778
779                     int2x = midpx - d2 * by;
780                     int2y = midpy + d2 * bx;
781
782                     for(s = 0; s < c1->num_painters; ++s)
783                     {
784                         paint(c1->painters + s, int1x, int1y, int2x, int2y, 
785                               dpy, window, fgc, f);
786                     }
787                 }
788             }
789         }
790         else /* f->draw_centers */
791         {
792             XDrawPoint(dpy, window, fgc, c1->x, c1->y);
793         }
794     }
795 }
796
797 struct state {
798   Display *dpy;
799   Window window;
800
801   unsigned int max_cycles;
802   int growth_delay;
803   GC fgc;
804   XGCValues gcv;
805   XWindowAttributes xgwa;
806
807   struct field *f;
808 };
809
810
811 static void *
812 interaggregate_init (Display *dpy, Window window)
813 {
814     struct state *st = (struct state *) calloc (1, sizeof(*st));
815
816 #ifdef TIME_ME
817     int frames;
818     struct timeval tm1, tm2;
819     double tdiff;
820 #endif
821
822     st->dpy = dpy;
823     st->window = window;
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"));
832
833     if (st->f->num_circles <= 1) 
834     {
835         fprintf(stderr, "%s: Minimum number of circles is 2\n", 
836                 progname);
837         exit (1);
838     }
839
840     if ( (st->f->percent_orbits < 0) || (st->f->percent_orbits > 100) )
841     {
842         fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n", 
843                 progname);
844         exit (1);
845     }
846
847     if ( (st->f->base_orbits < 0) || (st->f->base_orbits > 100) )
848     {
849         fprintf(stderr, "%s: base-oribts must be between 0 and 100\n", 
850                 progname);
851         exit (1);
852     }
853
854     if ( st->f->percent_orbits == 100 )
855         st->f->base_on_center = True;
856
857     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
858
859     build_colors(st->f, st->dpy, &st->xgwa);
860
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");
865
866     st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
867
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;
873
874     /* Initialize stuff */
875     build_field(st->dpy, st->window, st->xgwa, st->fgc, st->f);
876
877 #ifdef TIME_ME
878     gettimeofday(&tm1, NULL);
879     frames = 0;
880 #endif
881
882     return st;
883 }
884
885
886 static unsigned long
887 interaggregate_draw (Display *dpy, Window window, void *closure)
888 {
889   struct state *st = (struct state *) closure;
890
891   if ((st->f->cycles % 10) == 0) 
892     {
893       /* Restart if the window size changes */
894       XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
895
896       if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) 
897         {
898           st->f->height = st->xgwa.height;
899           st->f->width = st->xgwa.width;
900           st->f->visdepth = st->xgwa.depth;
901
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);
906         }
907     }
908
909   moveCircles(st->f);
910   drawIntersections(st->dpy, st->window, st->fgc, st->f);
911
912   st->f->cycles++;
913
914
915   if (st->f->cycles >= st->max_cycles && st->max_cycles != 0)
916     {
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);
921     }
922
923 #ifdef TIME_ME
924   frames++;
925   gettimeofday(&tm2, NULL);
926
927   tdiff = (tm2.tv_sec - tm1.tv_sec) 
928     + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
929
930   if ( tdiff > 1 )
931     {
932       fprintf(stderr, "fps: %d %f %f\n", 
933               frames, tdiff, frames / tdiff );
934
935       fprintf(stderr, "intersections: %d %d %f\n", 
936               f->intersection_count, f->possible_intersections, 
937               ((double)f->intersection_count) / 
938               f->possible_intersections);
939
940       fprintf(stderr, "fpi: %f\n", 
941               ((double)frames) / f->intersection_count );
942
943       frames = 0;
944       tm1.tv_sec = tm2.tv_sec;
945       tm1.tv_usec = tm2.tv_usec;
946
947       f->intersection_count = f->possible_intersections = 0;
948     }
949 #endif
950
951   return st->growth_delay;
952 }
953
954
955 static void
956 interaggregate_reshape (Display *dpy, Window window, void *closure, 
957                  unsigned int w, unsigned int h)
958 {
959 }
960
961 static Bool
962 interaggregate_event (Display *dpy, Window window, void *closure, XEvent *event)
963 {
964   struct state *st = (struct state *) closure;
965   if (screenhack_event_helper (dpy, window, event))
966     {
967       st->f->height--;  /* act like a resize */
968       return True;
969     }
970   return False;
971 }
972
973 static void
974 interaggregate_free (Display *dpy, Window window, void *closure)
975 {
976   struct state *st = (struct state *) closure;
977   free (st);
978 }
979
980
981 XSCREENSAVER_MODULE ("Interaggregate", interaggregate)