http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.23.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 "screenhack.h"
54 #include <X11/Xutil.h>
55 #include <stdio.h>
56 #include <sys/time.h>
57
58 #ifndef MAX_WIDTH
59 #include <limits.h>
60 #define MAX_WIDTH SHRT_MAX
61 #endif
62
63 /* XXX take this out later */
64
65
66 #ifdef TIME_ME
67 #include <sys/time.h>
68 #endif
69
70 #include <math.h>
71
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)
75 #undef inline
76 #define inline                  /* */
77 #endif
78
79 #ifndef MIN
80 #define MIN(x,y) ((x < y) ? x : y)
81 #endif
82
83 #ifndef MAX
84 #define MAX(x,y) ((x < y) ? y : x)
85 #endif
86
87 char *progclass = "inter-aggregate";
88
89 char *defaults[] = 
90 {
91     ".background: white",
92     ".foreground: black",
93     "*maxCycles: 100000",
94 #ifdef TIME_ME
95     "*growthDelay: 0",
96 #else
97     "*growthDelay: 18000", 
98 #endif
99     "*numCircles: 100",
100     "*percentOrbits: 0",
101     "*baseOrbits: 75",
102     0
103 };
104
105 XrmOptionDescRec options[] = 
106 {
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"}, 
116     {0, 0, 0, 0}
117 };
118
119 /* Raw colormap extracted from pollockEFF.gif */
120 #if 0
121 char *rgb_colormap[] = 
122 {
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 */
131     0
132 };
133 #endif
134
135 char *rgb_colormap[] = 
136 {
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 */
143     "rgb:e6/d3/ae",
144     0
145 };
146
147 /* black white brown olive grey camel */
148
149 typedef enum { LINEAR, ORBIT } PathType;
150
151 typedef struct 
152 {
153
154     unsigned long color;
155     double gain;
156     double p;
157
158 } SandPainter;
159
160 typedef struct _circle
161 {
162     double radius;
163
164     double x;
165     double y;
166
167     PathType path_type;
168
169     /* for a linear path */
170     double dx;
171     double dy;
172
173     /* for orbital path */
174     double theta;
175     double r; 
176     double dtheta;
177     
178     struct _circle* center;
179
180     int num_painters;
181     SandPainter* painters;
182
183 } Circle;
184
185
186 struct field 
187 {
188     int height;
189     int width;
190
191     int num_circles;
192     Circle* circles;
193
194     int percent_orbits;
195     int base_orbits;
196     Bool base_on_center;
197
198     /* used for orbits circling the center of the screen */
199     Circle center_of_universe;
200
201     /* Raw map of pixels we need to keep for alpha blending */
202     unsigned long int *off_img;
203    
204     /* color parms */
205     int numcolors;
206     unsigned long *parsedcolors;
207     unsigned long fgcolor;
208     unsigned long bgcolor;
209     int visdepth;
210
211     unsigned int cycles;
212
213     double max_gain;
214
215     /* for debugging */
216     Bool draw_centers;
217
218     /* for profiling whatnot */ 
219     int possible_intersections;
220     int intersection_count;
221 };
222
223
224 struct field 
225 *init_field(void)
226 {
227     struct field *f = (struct field*) malloc(sizeof(struct field));
228     if ( f == NULL )
229     {
230         fprintf(stderr, "%s: Failed to allocate field!\n", progname);
231         exit(1);
232     }
233
234     f->height = 0;
235     f->width = 0;
236     f->num_circles = 0;
237     f->circles = NULL;
238     f->percent_orbits = 0;
239     f->base_orbits = 0;
240     f->base_on_center = False;
241     f->off_img = NULL;
242     f->numcolors = 0;
243     f->parsedcolors = NULL;
244     f->fgcolor = 0;
245     f->bgcolor = 0;
246     f->visdepth = 0;
247
248     f->cycles = 0;
249
250     f->max_gain = 0.22;
251
252     f->draw_centers = False;
253
254     f->possible_intersections = 0;
255     f->intersection_count = 0;
256
257     return f;
258 }
259
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)])
262
263 #define in_bounds(f, x, y) ((x >= 0) && (x < f->width) && (y >= 0) && (y < f->height))
264
265 /* Consider rewriting with XQueryColor, or ImageByteOrder */
266
267 inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) 
268 {
269     switch(depth) 
270     {
271     case 32:
272     case 24:
273         *b = c & 0xff; 
274         *g = (c & 0xff00) >> 8; 
275         *r = (c & 0xff0000) >> 16; 
276         break;
277     case 16:
278         *b = (c & 0x1f) << 3; 
279         *g = ((c >> 5) & 0x3f) << 2;
280         *r = ((c >> 11) & 0x1f) << 3; 
281         break;
282     case 15:
283         *b = (c & 0x1f) << 3;
284         *g = ((c >> 5) & 0x1f) << 3;
285         *r = ((c >> 10) & 0x1f) << 3;
286         break;
287     }
288 }
289
290 inline unsigned long rgb2point(int depth, int r, int g, int b) 
291 {
292     unsigned long ret = 0;
293
294     switch(depth) 
295     {
296     case 32:
297         ret = 0xff000000;
298     case 24:
299         ret |= (r << 16) | (g << 8) | b;
300         break;
301     case 16:
302         ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
303         break;
304     case 15:
305         ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
306         break;
307     }
308
309     return ret;
310 }
311
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, 
315                                  struct field *f) 
316 {
317     if (a >= 1.0) 
318     {
319         ref_pixel(f, x1, y1) = myc;
320         return myc;
321     } 
322     else 
323     {
324         int or=0, og=0, ob=0;
325         int r=0, g=0, b=0;
326         int nr, ng, nb;
327         unsigned long c;
328
329         c = ref_pixel(f, x1, y1);
330
331         point2rgb(f->visdepth, c, &or, &og, &ob);
332         point2rgb(f->visdepth, myc, &r, &g, &b);
333
334         nr = or + (r - or) * a;
335         ng = og + (g - og) * a;
336         nb = ob + (b - ob) * a;
337
338         c = rgb2point(f->visdepth, nr, ng, nb);
339
340         ref_pixel(f, x1, y1) = c;
341
342         return c;
343     }
344 }
345
346 inline void drawPoint(int x, int y, unsigned long color, double intensity,
347                       Display *dpy, Window window, GC fgc, struct field *f)
348                
349 {
350     unsigned long c;
351
352     if ( x >= f->width ) x -= f->width;
353     else if ( x < 0 ) x += f->width;
354         
355     if ( y >= f->height ) y -= f->height;
356     else if ( y < 0 ) y += f->height;
357
358     /* if ( in_bounds(f, x, y) ) ... */
359
360     c = trans_point(x, y, color, intensity, f);
361
362     XSetForeground(dpy, fgc, c);
363     XDrawPoint(dpy, window, fgc, x, y);
364 }
365
366 inline void paint(SandPainter* painter, double ax, double ay, double bx, double by,
367                   Display *dpy, Window window, GC fgc, 
368                   struct field *f)
369 {
370     /* the sand painter */
371
372     double inc, sandp;
373     int i;
374
375     /* XXX try adding tpoint here, like orig */
376
377     /* jitter the painter's values */
378     painter->gain += frand(0.05) - 0.025;
379     
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;
384
385     painter->p += frand(0.1) - 0.05;
386     
387     if ( 0 < painter->p )
388         painter->p = 0;
389     else if ( painter->p > 1.0 )
390         painter->p = 1.0;
391
392     /* replace 0.1 with 1 / f->grains */
393     inc = painter->gain * 0.1;
394     sandp = 0;
395
396     for(i = 0; i <= 10; ++i)
397     {
398         int drawx, drawy;
399         double sp, sm;
400         double intensity = 0.1 - 0.009 * i;
401
402         sp = sin(painter->p + sandp);
403         drawx = ax + (bx - ax) * sp;
404         drawy = ay + (by - ay) * sp;
405
406         drawPoint(drawx, drawy, painter->color,
407                   intensity,
408                   dpy, window, fgc, f);
409
410         sm = sin(painter->p - sandp);
411         drawx = ax + (bx - ax) * sm;
412         drawy = ay + (by - ay) * sm;
413
414         drawPoint(drawx, drawy, painter->color,
415                   intensity,
416                   dpy, window, fgc, f);
417
418         sandp += inc;
419     }
420 }
421
422 void build_colors(struct field *f, Display *dpy, XWindowAttributes *xgwa) 
423 {
424
425     XColor tmpcolor;
426     int i;
427     /* Count the colors in our map and assign them in a horrifically inefficient 
428      * manner but it only happens once */
429
430     for( f->numcolors = 0; 
431          rgb_colormap[f->numcolors] != NULL; 
432          ++f->numcolors )
433     {
434         ;
435     }
436
437     f->parsedcolors = (unsigned long *) calloc(f->numcolors,
438                                                sizeof(unsigned long));
439     if ( f->parsedcolors == NULL )
440     {
441         fprintf(stderr, "%s: Failed to allocate parsedcolors\n",
442                 progname);
443         exit(1);
444     }
445         
446     for(i = 0; i < f->numcolors; ++i)
447     {
448         if (!XParseColor(dpy, xgwa->colormap, 
449                          rgb_colormap[i], &tmpcolor)) 
450         {
451             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
452                     rgb_colormap[i]);
453             exit(1);
454         }
455
456         if (!XAllocColor(dpy, xgwa->colormap, &tmpcolor)) 
457         {
458             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
459                     rgb_colormap[i]);
460             exit(1);
461         }
462
463         f->parsedcolors[i] = tmpcolor.pixel;
464
465     }
466 }
467
468 /* used when the window is resized */
469 void build_img(struct field *f)
470 {
471     if (f->off_img) {
472         free(f->off_img);
473         f->off_img = NULL;
474     }
475
476     f->off_img = (unsigned long *) calloc(f->width * f->height,
477                                           sizeof(unsigned long));
478                                            
479
480     if ( f->off_img == NULL )
481     {
482         fprintf(stderr, "%s: Failed to allocate off_img\n",
483                 progname);
484         exit(1);
485     }
486
487     memset(f->off_img, f->bgcolor, 
488            sizeof(unsigned long) * f->width * f->height);
489 }
490
491 void free_circles(struct field *f) 
492 {
493     int i;
494
495     if ( f->circles != NULL )
496     {
497         for(i = 0; i < f->num_circles; ++i)
498         {
499             free (f->circles[i].painters);
500         }
501
502         free (f->circles);
503         f->circles = NULL;
504     }
505 }
506
507 void build_field(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
508                  struct field *f) 
509 {
510     int i;
511     int num_orbits;
512     int base_orbits;
513     int orbit_start;
514     build_img(f);
515
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;
519
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;
523
524     free_circles(f);
525
526     f->circles = (Circle*) calloc(f->num_circles, sizeof(Circle));
527     if ( f->circles == NULL )
528     {
529         fprintf(stderr, "%s: Failed to allocate off_img\n",
530                 progname);
531         exit(1);
532     }
533
534     for(i = 0; i < f->num_circles; ++i)
535     {
536         int j;
537         Circle *circle = f->circles + i;
538
539         /* make this a pref */
540
541         if ( i >= orbit_start )
542           circle->path_type = ORBIT;
543         else
544           circle->path_type = LINEAR;
545
546
547         if ( circle->path_type == LINEAR )
548         {
549             circle->x = frand(f->width);
550             circle->y = frand(f->height);
551
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);
557
558             /* in case we want orbits based on lines */
559             circle->r = MIN(f->width, f->height) / 2.0;
560             circle->center = NULL;
561         }
562         else /* == ORBIT */
563         {
564             if (i < base_orbits )
565             {
566                 if ( f->base_on_center )
567                     circle->center = &f->center_of_universe; 
568                 else
569                 {
570                     circle->center = f->circles + 
571                         ((int)frand(orbit_start - 0.1));
572                 }
573
574                 circle->r = 1 + frand(MIN(f->width, f->height) / 2.0);
575
576                 /* circle->radius = 5 + frand(55); */
577             }
578             else
579             {
580                 /* give a preference for the earlier circles */
581
582                 double p = frand(0.9);
583
584                 circle->center = f->circles + (int) (p*i);
585
586                 circle->r = 1 + 0.5 * circle->center->r + 0.5 * frand(circle->center->r); 
587                 /* circle->r = 1 + frand(circle->center->r / 2); */
588
589
590                 /* circle->radius = MAX(5, frand(circle->r)); */
591                 /* circle->radius = 5 + frand(55); */
592             }
593
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);
597
598             circle->x = circle->r * cos(circle->theta) + circle->center->x;
599             circle->y = circle->r * sin(circle->theta) + circle->center->y;
600
601         }
602
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 )
608         {
609             fprintf(stderr, "%s: failed to allocate painters", progname);
610             exit(1);
611         }
612
613         for(j = 0; j < circle->num_painters; ++j)
614         {
615             SandPainter *painter = circle->painters + j;
616
617             painter->gain = frand(0.09) + 0.01;
618             painter->p = frand(1.0);
619             painter->color = 
620                 f->parsedcolors[(int)(frand(0.999) * f->numcolors)];
621         }
622     }
623 }
624
625 void moveCircles(struct field *f)
626 {
627     int i;
628
629     for(i = 0; i < f->num_circles; ++i)
630     {
631         Circle *circle = f->circles + i;
632
633         if ( circle->path_type == LINEAR )
634         {
635             circle->x += circle->dx;
636             circle->y += circle->dy;
637
638 #if 0
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;
643
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;
648 #else
649             if ( circle->x < 0 ) circle->x += f->width;
650             else if ( circle->x >= f->width ) circle->x -= f->width;
651
652             if ( circle->y < 0 ) circle->y += f->height;
653             else if ( circle->y >= f->height ) circle->y -= f->height;
654 #endif
655         }
656         else /* (circle->path_type == ORBIT) */
657         {
658             circle->theta += circle->dtheta;
659
660             if ( circle->theta < 0 ) circle->theta += 2 * M_PI;
661             else if ( circle->theta > 2 * M_PI ) circle->theta -= 2 * M_PI;
662
663             circle->x = circle->r * cos(circle->theta) + circle->center->x;
664             circle->y = circle->r * sin(circle->theta) + circle->center->y;
665
666 #if 0
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;
671
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;
676 #else
677             if ( circle->x < 0 ) circle->x += f->width;
678             else if ( circle->x >= f->width ) circle->x -= f->width;
679
680             if ( circle->y < 0 ) circle->y += f->height;
681             else if ( circle->y >= f->height ) circle->y -= f->height;
682 #endif
683         }
684     }
685 }
686
687 void drawIntersections(Display *dpy, Window window, GC fgc, struct field *f)
688 {
689     int i,j;
690
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,
694      * etc'
695      *
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.
700      * 
701      * But hey, if you're bored, go have fun.  Let me know how it
702      * turns out.
703      */
704
705
706     for(i = 0; i < f->num_circles; ++i)
707     {
708         Circle *c1 = f->circles + i;
709
710         if ( !f->draw_centers )
711         {
712             /* the default branch */
713
714             for(j = i + 1; j < f->num_circles; ++j)
715             {
716                 double d, dsqr, dx, dy;
717                 Circle *c2 = f->circles + j;
718
719                 ++f->possible_intersections;
720                 dx = c2->x - c1->x;
721                 dy = c2->y - c1->y;
722
723                 dsqr = dx * dx + dy * dy;
724                 d = sqrt(dsqr);
725
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) ) )
730                 {
731                     double d1, d2, r1sqr; 
732                     double bx, by;
733                     double midpx, midpy;
734                     double int1x, int1y;
735                     double int2x, int2y;
736                     int s;
737
738                     /* woo-hoo.  the circles are neither outside nor
739                      * inside each other.  they intersect.  
740                      *
741                      * Now, compute the coordinates of the points of
742                      * intersection 
743                      */
744
745                     ++f->intersection_count;
746
747                     /* unit vector in direction of c1 to c2 */
748                     bx = dx / d;
749                     by = dy / d;
750
751                     r1sqr = c1->radius * c1->radius;
752
753                     /* distance from c1's center midpoint of intersection
754                      * points */
755
756                     d1 = 0.5 * (r1sqr - c2->radius * c2->radius + dsqr) / d;
757                 
758                     midpx = c1->x + d1 * bx;
759                     midpy = c1->y + d1 * by;
760
761                     /* distance from midpoint to points of intersection */
762
763                     d2 = sqrt(r1sqr - d1 * d1);
764
765                     int1x = midpx + d2 * by;
766                     int1y = midpy - d2 * bx;
767
768                     int2x = midpx - d2 * by;
769                     int2y = midpy + d2 * bx;
770
771                     for(s = 0; s < c1->num_painters; ++s)
772                     {
773                         paint(c1->painters + s, int1x, int1y, int2x, int2y, 
774                               dpy, window, fgc, f);
775                     }
776                 }
777             }
778         }
779         else /* f->draw_centers */
780         {
781             XDrawPoint(dpy, window, fgc, c1->x, c1->y);
782         }
783     }
784 }
785
786
787 void screenhack(Display * dpy, Window window)
788 {
789     struct field *f = init_field();
790
791     unsigned int max_cycles = 0;
792     int growth_delay = 0;
793
794     GC fgc;
795     XGCValues gcv;
796     XWindowAttributes xgwa;
797
798 #ifdef TIME_ME
799     int frames;
800     struct timeval tm1, tm2;
801     double tdiff;
802 #endif
803
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"));
811
812     if (f->num_circles <= 1) 
813     {
814         fprintf(stderr, "%s: Minimum number of circles is 2\n", 
815                 progname);
816         return;
817     }
818
819     if ( (f->percent_orbits < 0) || (f->percent_orbits > 100) )
820     {
821         fprintf(stderr, "%s: percent-oribts must be between 0 and 100\n", 
822                 progname);
823         return;
824     }
825
826     if ( (f->base_orbits < 0) || (f->base_orbits > 100) )
827     {
828         fprintf(stderr, "%s: base-oribts must be between 0 and 100\n", 
829                 progname);
830         return;
831     }
832
833     if ( f->percent_orbits == 100 )
834         f->base_on_center = True;
835
836     XGetWindowAttributes(dpy, window, &xgwa);
837
838     build_colors(f, dpy, &xgwa);
839
840     gcv.foreground = get_pixel_resource("foreground", "Foreground",
841                                         dpy, xgwa.colormap);
842     gcv.background = get_pixel_resource("background", "Background",
843                                         dpy, xgwa.colormap);
844
845     fgc = XCreateGC(dpy, window, GCForeground, &gcv);
846
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;
852
853     /* Initialize stuff */
854     build_field(dpy, window, xgwa, fgc, f);
855
856 #ifdef TIME_ME
857     gettimeofday(&tm1, NULL);
858     frames = 0;
859 #endif
860
861     while (1) 
862     {
863         if ((f->cycles % 10) == 0) 
864         {
865             /* Restart if the window size changes */
866             XGetWindowAttributes(dpy, window, &xgwa);
867
868             if (f->height != xgwa.height || f->width != xgwa.width) 
869             {
870                 f->height = xgwa.height;
871                 f->width = xgwa.width;
872                 f->visdepth = xgwa.depth;
873
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);
878             }
879             screenhack_handle_events(dpy);
880         }
881
882         moveCircles(f);
883         drawIntersections(dpy, window, fgc, f);
884
885         f->cycles++;
886
887
888         XSync(dpy, False);
889
890         screenhack_handle_events(dpy);
891
892         if (f->cycles >= max_cycles && max_cycles != 0)
893         {
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);
898         }
899
900         if (growth_delay)
901             usleep(growth_delay);
902
903 #ifdef TIME_ME
904         frames++;
905         gettimeofday(&tm2, NULL);
906
907         tdiff = (tm2.tv_sec - tm1.tv_sec) 
908             + (tm2.tv_usec - tm1.tv_usec) * 0.00001;
909
910         if ( tdiff > 1 )
911         {
912             fprintf(stderr, "fps: %d %f %f\n", 
913                     frames, tdiff, frames / tdiff );
914
915             fprintf(stderr, "intersections: %d %d %f\n", 
916                     f->intersection_count, f->possible_intersections, 
917                     ((double)f->intersection_count) / 
918                     f->possible_intersections);
919
920             fprintf(stderr, "fpi: %f\n", 
921                     ((double)frames) / f->intersection_count );
922
923             frames = 0;
924             tm1.tv_sec = tm2.tv_sec;
925             tm1.tv_usec = tm2.tv_usec;
926
927             f->intersection_count = f->possible_intersections = 0;
928         }
929 #endif
930     }
931 }
932