http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / piecewise.c
1 /* piecewise, 21jan2003
2  * Geoffrey Irving <irving@caltech.edu>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include <stdarg.h>
14 #include <math.h>
15 #include "screenhack.h"
16
17 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
18 # include "xdbe.h"
19 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
20
21 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
22 #undef inline
23 #define inline                  /* */
24 #endif
25
26 #define X_PI (180 * 64)
27
28 #define START 0
29 #define CROSS 1
30 #define FINISH 2
31
32 #define ARC_BUFFER_SIZE 256
33
34
35 typedef struct _tree {
36   struct _tree *l, *r;   /* left and right children */
37                          /* extra stuff would go here */
38   } tree;
39
40
41 struct _fringe;
42
43 typedef struct _circle {
44   int r;                    /* radius */
45   double x, y;              /* position */
46   double dx, dy;            /* velocity */
47
48   int visible;              /* default visibility */
49   struct _fringe *lo, *hi;  /* lo and hi fringes */
50
51   int ni;                   /* number of intersections */
52   int *i;                   /* sorted intersection list */
53   } circle;             
54
55 typedef struct _fringe {
56   struct _fringe *l, *r;  /* left and right children for splay trees */
57
58   circle *c;              /* associated circle */
59   int side;               /* 0 for lo, 1 for hi */
60
61   int mni;                /* size of intersection array */
62   int ni;                 /* number of intersections */
63   int *i;                 /* sorted intersection list */
64   } fringe;
65
66
67 typedef struct _event {
68   struct _event *l, *r;    /* left and right children for splay tree */
69
70   int kind;                /* type of event */
71   double x, y;             /* position */
72   fringe *lo, *hi;         /* fringes */
73   } event;
74
75
76 struct state {
77   Display *dpy;
78   Window window;
79
80   double event_cut_y;
81
82   double fringe_start_cut_x;
83   double fringe_start_cut_y;
84
85   double fringe_double_cut_x;
86   double fringe_double_cut_y;
87   fringe *fringe_double_cut_lo;
88   fringe *fringe_double_cut_hi;
89
90   int arc_buffer_count;
91   XArc arc_buffer[ARC_BUFFER_SIZE];
92
93   Bool dbuf;
94   XColor *colors;
95   XGCValues gcv;
96   GC erase_gc, draw_gc;
97   XWindowAttributes xgwa;
98   Pixmap b, ba, bb;    /* double-buffering pixmap */
99
100 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
101   XdbeBackBuffer backb;
102 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
103
104   int count, delay, ncolors, colorspeed, color_index, flags, iterations;
105   int color_iterations;
106   circle *circles;
107 };
108
109 typedef int (*cut)(struct state *, tree*);    /* cut x is <, =, or > 0 given a <, =, or > x for some a */
110
111
112
113 /******** splaying code */
114
115 /* Top-down splay routine.  Reference:
116  *   "Self-adjusting Binary Search Trees", Sleator and Tarjan,
117  *   JACM Volume 32, No 3, July 1985, pp 652-686.
118  * See page 668 for specific splay transformations */
119
120 static tree *splay(struct state *st, cut c, tree *t)
121 {
122   int v, vv;
123   tree *l, *r;
124   tree **lr, **rl;
125   tree *x, *y, *z;
126
127   if (!t)
128     return 0;
129
130   /* initialization */
131   x = t;
132   l = r = 0;
133   lr = &l;
134   rl = &r;
135
136   /* top-down splaying loop */
137   for (;;) {
138     v = c(st, x);
139     if (v == 0)
140       break;               /*** success ***/
141     else if (v < 0) {
142       y = x->l;
143       if (!y)
144         break;             /*** trivial ***/
145       else {
146         vv = c(st, y);
147         if (vv == 0) {
148           *rl = x;         /*** zig ***/
149           rl = &x->l;
150           x = y;
151           break;
152           }
153         else if (vv < 0) {
154           z = y->l;
155           if (!z) {
156             *rl = x;       /*** zig ***/
157             rl = &x->l;
158             x = y;
159             break;
160             }
161           else {
162             x->l = y->r;   /*** zig-zig ***/
163             y->r = x;
164             *rl = y;
165             rl = &y->l;
166             x = z;
167             }
168           }
169         else { /* vv > 0 */
170           z = y->r;
171           if (!z) {
172             *rl = x;       /*** zig ***/
173             rl = &x->l;
174             x = y;
175             break;
176             }
177           else {           /*** zig-zag ***/
178             *lr = y;
179             lr = &y->r;
180             *rl = x;
181             rl = &x->l;
182             x = z;
183             }
184           }
185         }
186       }
187     else { /* v > 0 */
188       y = x->r;
189       if (!y)
190         break;             /*** trivial ***/
191       else {
192         vv = c(st, y);
193         if (vv == 0) {
194           *lr = x;         /*** zig ***/
195           lr = &x->r;
196           x = y;
197           break;
198           }
199         else if (vv > 0) {
200           z = y->r;
201           if (!z) {
202             *lr = x;       /*** zig ***/
203             lr = &x->r;
204             x = y;
205             break;
206             }
207           else {
208             x->r = y->l;   /*** zig-zig ***/
209             y->l = x;
210             *lr = y;
211             lr = &y->r;
212             x = z;
213             }
214           }
215         else { /* vv < 0 */
216           z = y->l;
217           if (!z) {
218             *lr = x;       /*** zig ***/
219             lr = &x->r;
220             x = y;
221             break;
222             }
223           else {           /*** zig-zag ***/
224             *rl = y;
225             rl = &y->l;
226             *lr = x;
227             lr = &x->r;
228             x = z;
229             }
230           }
231         }
232       }
233     }
234
235   /* completion */
236   *lr = x->l;
237   x->l = l;
238   *rl = x->r;
239   x->r = r;
240   return x;
241   }
242
243 static tree *splay_min(tree *t)
244 {
245   tree *r, **rl;
246   tree *x, *y, *z;
247
248   if (!t)
249     return 0;
250
251   x = t;
252   r = 0;
253   rl = &r;
254
255   for (;;) {
256     y = x->l;
257     if (!y)
258       break;           /*** trivial ***/
259     else {
260       z = y->l;
261       if (!z) {
262         *rl = x;       /*** zig ***/
263         rl = &x->l;
264         x = y;
265         break;
266         }
267       else {
268         x->l = y->r;   /*** zig-zig ***/
269         y->r = x;
270         *rl = y;
271         rl = &y->l;
272         x = z;
273         }
274       }
275     }
276
277   x->l = 0;
278   *rl = x->r;
279   x->r = r;
280   return x;
281   }
282
283 static tree *splay_max(tree *t)
284 {
285   tree *l, **lr;
286   tree *x, *y, *z;
287
288   if (!t)
289     return 0;
290
291   x = t;
292   l = 0;
293   lr = &l;
294
295   for (;;) {
296     y = x->r;
297     if (!y)
298       break;           /*** trivial ***/
299     else {
300       z = y->r;
301       if (!z) {
302         *lr = x;       /*** zig ***/
303         lr = &x->r;
304         x = y;
305         break;
306         }
307       else {
308         x->r = y->l;   /*** zig-zig ***/
309         y->l = x;
310         *lr = y;
311         lr = &y->r;
312         x = z;
313         }
314       }
315     }
316
317   *lr = x->l;
318   x->l = l;
319   x->r = 0;
320   return x;
321   }
322
323 /******** circles and fringe */
324
325 static inline double fringe_x(fringe *f, double y)
326 {
327   double dy, d;
328   dy = f->c->y - y;
329   d = sqrt(f->c->r * f->c->r - dy * dy);
330   return f->side ? f->c->x + d : f->c->x - d;
331   }
332
333 static inline void fringe_add_intersection(fringe *f, double x, double y)
334 {
335   f->ni++;
336   if (f->mni < f->ni) {
337     f->mni += 2;
338     f->i = realloc(f->i, sizeof(int) * f->mni);
339     }
340   f->i[f->ni-1] = rint(atan2(y - f->c->y, x - f->c->x) * X_PI / M_PI);
341   }
342
343 static circle *init_circles(struct state *st, int n, int w, int h)
344 {
345   int i, r0, dr, speed;
346   double v, a;
347   double minradius, maxradius;
348   fringe *s = malloc(sizeof(fringe) * n * 2);    /* never freed */
349   circle *c = malloc(sizeof(circle) * n);
350
351   speed = get_integer_resource(st->dpy, "speed", "Speed");
352   minradius = get_float_resource(st->dpy, "minradius", "Float");
353   maxradius = get_float_resource(st->dpy, "maxradius", "Float");
354   if (maxradius < minradius)
355     maxradius = minradius;
356
357   r0 = ceil(minradius * h); 
358   dr = floor(maxradius * h) - r0 + 1;
359
360   for (i=0;i<n;i++) {
361     c[i].r = r0 + ((dr > 0) ? random() % dr : 0);
362     c[i].x = c[i].r + frand(w - 1 - 2 * c[i].r);
363     c[i].y = c[i].r + frand(h - 1 - 2 * c[i].r);
364     c[i].visible = random() & 1;
365
366     c[i].ni = 0;
367     c[i].i = 0;
368
369     a = frand(2 * M_PI);
370     v = (1 + frand(0.5)) * speed / 10.0;
371     c[i].dx = v * cos(a);
372     c[i].dy = v * sin(a);
373
374     c[i].lo = s+i+i;
375     c[i].hi = s+i+i+1;
376     c[i].lo->c = c[i].hi->c = c+i;
377     c[i].lo->side = 0;
378     c[i].hi->side = 1;
379     c[i].lo->mni = c[i].lo->ni = c[i].hi->mni = c[i].hi->ni = 0;
380     c[i].lo->i = c[i].hi->i = 0;
381     }
382
383   return c;
384   }
385
386 /* this is a hack, but I guess that's what I writing anyways */
387 static void tweak_circle(circle *c)
388 {
389   c->x += frand(2) - 1;
390   c->y += frand(1) + 0.1;
391   }
392
393 static void move_circle(circle *c, int w, int h)
394 {
395   c->x += c->dx;
396   if (c->x < c->r) {
397     c->x = c->r;
398     c->dx = -c->dx;
399     }
400   else if (c->x >= w - c->r) {
401     c->x = w - 1 - c->r;
402     c->dx = -c->dx;
403     }
404   c->y += c->dy;
405   if (c->y < c->r) {
406     c->y = c->r;
407     c->dy = -c->dy;
408     }
409   else if (c->y >= h - c->r) {
410     c->y = h - 1 - c->r;
411     c->dy = -c->dy;
412     }
413   }
414
415 /******** event queue */
416
417 static int event_cut(struct state *st, event *e)
418 {
419   return st->event_cut_y == e->y ? 0 : st->event_cut_y < e->y ? -1 : 1;
420   }
421
422 static void event_insert(struct state *st, event **eq, event *e)
423 {
424   if (!*eq) {
425     e->l = e->r = 0;
426     *eq = e;
427     return; /* avoid leak */
428     }
429
430   st->event_cut_y = e->y;
431   *eq = (event*)splay(st, (cut)event_cut, (tree*)*eq);
432
433   if (e->y == (*eq)->y) {
434     if (!((e->lo == (*eq)->lo && e->hi == (*eq)->hi) || (e->lo == (*eq)->hi && e->hi == (*eq)->lo))) {
435       e->l = (*eq)->l;
436       e->r = 0;             /* doing this instead of dying might be dangerous */
437       (*eq)->l = e;           
438       }
439     else
440       free(e); /* don't leak! */
441     }
442   else if (e->y < (*eq)->y) {
443     e->l = (*eq)->l;
444     e->r = *eq;
445     (*eq)->l = 0;
446     *eq = e;
447     }
448   else {
449     e->l = *eq;
450     e->r = (*eq)->r; 
451     (*eq)->r = 0;
452     *eq = e;
453     }
454   }
455
456 static void circle_start_event(struct state *st, event **eq, circle *c)
457 {
458   event *s;
459   s = malloc(sizeof(event));
460   s->kind = START;
461   s->x = c->x;
462   s->y = c->y - c->r;
463   s->lo = c->lo;
464   s->hi = c->hi;
465   event_insert(st, eq, s);
466   }
467
468 static void circle_finish_event(struct state *st, event **eq, circle *c)
469 {
470   event *f;
471   f = malloc(sizeof(event));
472   f->kind = FINISH;
473   f->x = c->x;
474   f->y = c->y + c->r;
475   f->lo = c->lo;
476   f->hi = c->hi;
477   event_insert(st, eq, f);
478   }
479
480 static event *event_next(event **eq)
481 {
482   event *e;
483   if (!*eq)
484     return 0;
485   else {
486     e = (event*)splay_min((tree*)*eq);
487     *eq = e->r; 
488     return e;
489     }
490   }
491
492 static void event_shred(event *e)
493 {
494   if (e) {
495     event_shred(e->l);
496     event_shred(e->r);
497     free(e);
498     }
499   }
500
501 /******** fringe intersection */
502
503 static inline int check_fringe_intersection(double ye, fringe *lo, fringe *hi, double x, double y)
504 {
505   return ye <= y && ((x < lo->c->x) ^ lo->side) && ((x < hi->c->x) ^ hi->side);
506   }
507
508 static void fringe_intersect(struct state *st, event **eq, double y, fringe *lo, fringe *hi)
509 {
510   event *e;
511   double dx, dy, sd, rs, rd, d, sx, sy, rp, sqd;
512   double x1, y1, x2, y2;
513
514   if (lo->c == hi->c)
515     return;
516
517   dx = hi->c->x - lo->c->x; 
518   dy = hi->c->y - lo->c->y; 
519   sd = dx * dx + dy * dy; 
520
521   if (sd == 0)
522     return;
523
524   rs = hi->c->r + lo->c->r; 
525   rd = hi->c->r - lo->c->r; 
526   d = (rd * rd - sd) * (sd - rs * rs);
527
528   if (d <= 0)
529     return;
530  
531   sd = 0.5 / sd;
532   rp = rs * rd; 
533   sqd = sqrt(d); 
534   sx = (lo->c->x + hi->c->x) / 2;
535   sy = (lo->c->y + hi->c->y) / 2;
536   x1 = sx + sd * (dy * sqd - dx * rp); 
537   y1 = sy - sd * (dx * sqd + dy * rp);
538   x2 = sx - sd * (dy * sqd + dx * rp);
539   y2 = sy + sd * (dx * sqd - dy * rp);
540
541   #define CHECK(xi, yi) (y <= yi && ((xi < lo->c->x) ^ lo->side) && ((xi < hi->c->x) ^ hi->side))
542
543   #define ADD_CROSS(xi, yi, ilo, ihi) {  \
544     e = malloc(sizeof(event));   /* #### LEAK */        \
545     e->kind = CROSS;                     \
546     e->x = xi; e->y = yi;                \
547     e->lo = ilo; e->hi = ihi;            \
548     event_insert(st, eq, e);                 \
549     }
550
551   if (CHECK(x1, y1)) {
552     if (CHECK(x2, y2)) {
553       if (y1 < y2) {
554         ADD_CROSS(x1, y1, lo, hi);
555         ADD_CROSS(x2, y2, hi, lo);
556         }
557       else {
558         ADD_CROSS(x1, y1, hi, lo);
559         ADD_CROSS(x2, y2, lo, hi);
560         }
561       }
562     else
563       ADD_CROSS(x1, y1, lo, hi);
564     }
565   else if (CHECK(x2, y2))
566     ADD_CROSS(x2, y2, lo, hi); 
567
568   return;
569   }
570
571 /******** fringe trees and event handling */
572
573 #define PANIC ((fringe*)1)     /* by alignment, no fringe should every be 1 */
574
575 static fringe *check_lo(struct state *st, event **eq, double y, fringe *f, fringe *hi)
576 {
577   if (f) {
578     f = (fringe*)splay_max((tree*)f);
579     fringe_intersect(st, eq, y, f, hi);
580     }
581   return f;
582   }
583
584 static fringe *check_hi(struct state *st, event **eq, double y, fringe *lo, fringe *f)
585 {
586   if (f) {
587     f = (fringe*)splay_min((tree*)f);
588     fringe_intersect(st, eq, y, lo, f);
589     }
590   return f;
591   }
592
593 static int fringe_start_cut(struct state *st, fringe *f)
594 {
595   double x = fringe_x(f, st->fringe_start_cut_y);
596   return st->fringe_start_cut_x == x ? 0 : st->fringe_start_cut_x < x ? -1 : 1;
597   }
598
599 static fringe *fringe_start(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
600 {
601   double sx;
602
603   if (!f) {
604     circle_finish_event(st, eq, lo->c);
605     lo->l = 0;
606     lo->r = hi;
607     hi->l = hi->r = 0;
608     return lo;
609     }
610
611   st->fringe_start_cut_x = x;
612   st->fringe_start_cut_y = y;
613   f = (fringe*)splay(st, (cut)fringe_start_cut, (tree*)f);
614
615   sx = fringe_x(f, y);
616   if (x == sx) {       /* time to cheat my way out of handling degeneracies */
617     tweak_circle(lo->c);
618     circle_start_event(st, eq, lo->c);
619     return f;
620     }
621   else if (x < sx) {
622     circle_finish_event(st, eq, lo->c);
623     f->l = check_lo(st, eq, y, f->l, lo);    
624     fringe_intersect(st, eq, y, hi, f);
625     lo->l = f->l;
626     lo->r = f;
627     f->l = hi;
628     hi->l = hi->r = 0;
629     return lo;
630     }
631   else {
632     circle_finish_event(st, eq, lo->c);
633     fringe_intersect(st, eq, y, f, lo);
634     f->r = check_hi(st, eq, y, hi, f->r);
635     hi->r = f->r;
636     hi->l = f;
637     f->r = lo;
638     lo->l = lo->r = 0;
639     return hi;
640     }
641   }
642
643 static int fringe_double_cut(struct state *st, fringe *f)
644 {
645   double x;
646   if (f == st->fringe_double_cut_lo || f == st->fringe_double_cut_hi)
647     return 0;
648   x = fringe_x(f, st->fringe_double_cut_y);
649   return st->fringe_double_cut_x == x ? 0 : st->fringe_double_cut_x < x ? -1 : 1;
650   }
651
652 static int fringe_double_splay(struct state *st, fringe *f, double x, double y, fringe *lo, fringe *hi)
653 {
654   st->fringe_double_cut_x = x;
655   st->fringe_double_cut_y = y;
656   st->fringe_double_cut_lo = lo;
657   st->fringe_double_cut_hi = hi;
658   f = (fringe*)splay(st, (cut)fringe_double_cut, (tree*)f);
659
660   if (f == lo)
661     return (f->r = (fringe*)splay_min((tree*)f->r)) == hi;
662   else if (f == hi)
663     return (f->l = (fringe*)splay_max((tree*)f->l)) == lo;
664   else
665     return 0;
666   }
667
668 static fringe *fringe_cross(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
669 {
670   fringe *l, *r;
671   if (!fringe_double_splay(st, f, x, y, lo, hi))
672     return PANIC;
673   l = check_lo(st, eq, y, lo->l, hi);
674   r = check_hi(st, eq, y, lo, hi->r);
675   lo->l = hi;
676   lo->r = r;
677   hi->l = l;
678   hi->r = 0;
679   return lo;
680   }
681
682 static fringe *fringe_finish(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
683 {
684   if (!fringe_double_splay(st, f, x, y, lo, hi))
685     return PANIC;
686   else if (!lo->l)
687     return hi->r;
688   else if (!hi->r)
689     return lo->l;
690   else {
691     lo->l = (fringe*)splay_max((tree*)lo->l);
692     hi->r = (fringe*)splay_min((tree*)hi->r);
693     fringe_intersect(st, eq, y, lo->l, hi->r);
694     lo->l->r = hi->r;
695     hi->r->l = 0;
696     return lo->l;
697     }
698   }
699
700 /******** plane sweep */
701
702 static void sweep(struct state *st, int n, circle *c)
703 {
704   int i;
705   event *eq, *e;
706   fringe *f;
707
708   RESTART:
709   #define CHECK_PANIC()                 \
710     if (f == PANIC) {                   \
711       free(e);                          \
712       event_shred(eq);                  \
713       for (i=0;i<n;i++) {               \
714         tweak_circle(c+i);              \
715         c[i].lo->ni = c[i].hi->ni = 0;  \
716         }                               \
717       goto RESTART;                     \
718       }
719
720   eq = 0;
721   for (i=0;i<n;i++)
722     circle_start_event(st, &eq, c+i); 
723   f = 0;
724
725   while ((e = event_next(&eq))) {
726     switch (e->kind) {
727       case START:
728         f = fringe_start(st, &eq, f, e->x, e->y, e->lo, e->hi);
729         break;
730       case CROSS:
731         f = fringe_cross(st, &eq, f, e->x, e->y, e->lo, e->hi);
732         CHECK_PANIC();
733         fringe_add_intersection(e->lo, e->x, e->y);
734         fringe_add_intersection(e->hi, e->x, e->y);
735         break;
736       case FINISH:
737         f = fringe_finish(st, &eq, f, e->x, e->y, e->lo, e->hi);
738         CHECK_PANIC();
739         break;
740       }
741     free(e);
742     }
743   }
744
745 /******** circle drawing */
746
747 static void adjust_circle_visibility(circle *c)
748 {
749   int i, j, n, a;
750   int *in;
751   n = c->lo->ni + c->hi->ni;
752   in = malloc(sizeof(int) * n);
753   for (i=0;i<c->hi->ni;i++)
754     in[i] = c->hi->i[i]; 
755   for (i=c->lo->ni-1;i>=0;i--)
756     in[n-i-1] = c->lo->i[i] > 0 ? c->lo->i[i] : c->lo->i[i] + 2 * X_PI;
757   c->lo->ni = c->hi->ni = 0;
758
759   i = j = 0;
760   a = 0;
761   while (i < n && j < c->ni)           /* whee */
762     a = (in[i] < c->i[j] ? in[i++] : c->i[j++]) - a;
763   while (i < n)
764     a = in[i++] - a;
765   while (j < c->ni)
766     a = c->i[j++] - a;
767
768   if (a > X_PI) 
769     c->visible = !c->visible;
770   free(c->i);  
771   c->ni = n;
772   c->i = in;
773   }
774
775 static void flush_arc_buffer(struct state *st, Drawable w, GC gc)
776 {
777   if (st->arc_buffer_count) {
778     XDrawArcs(st->dpy, w, gc, st->arc_buffer, st->arc_buffer_count);
779     st->arc_buffer_count = 0;
780     }
781   }
782
783 static void draw_circle(struct state *st, Drawable w, GC gc, circle *c)
784 {
785   int i, xi, yi, di;
786   adjust_circle_visibility(c); 
787
788   xi = rint(c->x - c->r);
789   yi = rint(c->y - c->r);
790   di = c->r << 1;
791
792   #define ARC(p, a1, a2) {                                \
793     if (((p) & 1) ^ c->visible) {                         \
794       st->arc_buffer[st->arc_buffer_count].x      = xi;           \
795       st->arc_buffer[st->arc_buffer_count].y      = yi;           \
796       st->arc_buffer[st->arc_buffer_count].width  = di;           \
797       st->arc_buffer[st->arc_buffer_count].height = di;           \
798       st->arc_buffer[st->arc_buffer_count].angle1 = -(a1);        \
799       st->arc_buffer[st->arc_buffer_count].angle2 = (a1) - (a2);  \
800       st->arc_buffer_count++;                                 \
801       if (st->arc_buffer_count == ARC_BUFFER_SIZE)            \
802         flush_arc_buffer(st, w, gc);                     \
803       }                                                   \
804     }
805
806   if (!c->ni)
807     ARC(0, 0, 2 * X_PI)
808   else
809     ARC(0, c->i[c->ni-1], c->i[0] + 2 * X_PI)
810   for (i=1;i<c->ni;i++)
811     ARC(i, c->i[i-1], c->i[i])
812   }
813
814 /******** toplevel */
815
816 static void
817 check_for_leaks (void)
818 {
819 #ifdef HAVE_SBRK
820   static unsigned long early_brk = 0;
821   unsigned long max = 30 * 1024 * 1024;  /* 30 MB */
822   int b = (unsigned long) sbrk(0);
823   if (early_brk == 0)
824     early_brk = b;
825   else if (b > early_brk + max)
826     {
827       fprintf (stderr, "%s: leaked %lu MB -- aborting!\n",
828                progname, ((b - early_brk) >> 20));
829       exit (1);
830     }
831 #endif /* HAVE_SBRK */
832 }
833
834 static void *
835 piecewise_init (Display *dd, Window ww)
836 {
837   struct state *st = (struct state *) calloc (1, sizeof(*st));
838   st->dpy = dd;
839   st->window = ww;
840
841   st->count = get_integer_resource(st->dpy, "count", "Integer");
842   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
843   st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
844   st->colorspeed = get_integer_resource(st->dpy, "colorspeed", "Integer");
845   st->dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean");
846
847 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
848   st->dbuf = False;
849 # endif
850
851   st->color_iterations = st->colorspeed ? 100 / st->colorspeed : 100000;
852   if (!st->color_iterations)
853     st->color_iterations = 1;
854
855   XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
856   st->colors = calloc(sizeof(XColor), st->ncolors);
857
858   if (get_boolean_resource(st->dpy, "mono", "Boolean")) {  
859     MONO:
860       st->ncolors = 1;
861       st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap, "foreground", "Foreground");
862     }
863   else {
864     make_color_loop(st->dpy, st->xgwa.colormap, 0, 1, 1, 120, 1, 1, 240, 1, 1, st->colors, &st->ncolors, True, False);
865     if (st->ncolors < 2)
866       goto MONO; 
867     }
868
869   if (st->dbuf) {
870 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
871     st->b = st->backb = xdbe_get_backbuffer(st->dpy, st->window, XdbeUndefined);
872 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
873     
874     if (!st->b) {
875       st->ba = XCreatePixmap(st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
876       st->bb = XCreatePixmap(st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
877       st->b = st->ba;
878       }
879     }
880   else
881     st->b = st->window;
882
883   /* erasure gc */
884   st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, "background", "Background");
885   st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &st->gcv);
886
887   /* drawing gc */
888   st->flags = GCForeground;
889   st->color_index = random() % st->ncolors;
890   st->gcv.foreground = st->colors[st->color_index].pixel;
891   st->draw_gc = XCreateGC(st->dpy, st->b, st->flags, &st->gcv);
892
893   /* initialize circles */
894   st->circles = init_circles(st, st->count, st->xgwa.width, st->xgwa.height);
895   
896   st->iterations = 0;
897
898   return st;
899 }
900
901 static unsigned long
902 piecewise_draw (Display *dpy, Window window, void *closure)
903 {
904   struct state *st = (struct state *) closure;
905   int i;
906
907   XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
908
909   sweep(st, st->count, st->circles);
910   for (i=0;i<st->count;i++) {
911     draw_circle(st, st->b, st->draw_gc, st->circles+i);
912     move_circle(st->circles+i, st->xgwa.width, st->xgwa.height);
913   }
914   flush_arc_buffer(st, st->b, st->draw_gc);
915
916   if (++st->iterations % st->color_iterations == 0) {
917     st->color_index = (st->color_index + 1) % st->ncolors;
918     XSetForeground(st->dpy, st->draw_gc, st->colors[st->color_index].pixel);
919   }
920
921 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
922   if (st->backb) {
923     XdbeSwapInfo info[1];
924     info[0].swap_window = st->window;
925     info[0].swap_action = XdbeUndefined;
926     XdbeSwapBuffers (st->dpy, info, 1);
927   }
928   else
929 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
930     if (st->dbuf) {
931       XCopyArea (st->dpy, st->b, st->window, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height, 0, 0);
932       st->b = (st->b == st->ba ? st->bb : st->ba);
933     }
934
935   check_for_leaks();
936   return st->delay;
937 }
938
939 static void
940 piecewise_reshape (Display *dpy, Window window, void *closure, 
941                  unsigned int w, unsigned int h)
942 {
943 }
944
945 static Bool
946 piecewise_event (Display *dpy, Window window, void *closure, XEvent *event)
947 {
948   return False;
949 }
950
951 static void
952 piecewise_free (Display *dpy, Window window, void *closure)
953 {
954   struct state *st = (struct state *) closure;
955   free (st);
956 }
957
958
959
960 static const char *piecewise_defaults [] = {
961   ".background:         black",
962   ".foreground:         white",
963   "*delay:              10000",
964   "*speed:              15",
965   "*ncolors:            256",
966   ".colorspeed:         10",
967
968   ".count:              32",
969   ".minradius:          0.05",
970   ".maxradius:          0.2",   
971
972   "*doubleBuffer:       True",
973 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
974   "*useDBE:             True",
975 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
976   0
977   };
978
979 static XrmOptionDescRec piecewise_options [] = {
980   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
981   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
982   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
983   { "-colorspeed",      ".colorspeed",  XrmoptionSepArg, 0 },
984
985   { "-count",           ".count",       XrmoptionSepArg, 0 },
986   { "-minradius",       ".minradius",   XrmoptionSepArg, 0 },
987   { "-maxradius",       ".maxradius",   XrmoptionSepArg, 0 },
988
989   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True" },
990   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
991   { 0, 0, 0, 0 }
992   };
993
994
995 XSCREENSAVER_MODULE ("Piecewise", piecewise)