summaryrefslogtreecommitdiffstats
path: root/hacks/piecewise.c
diff options
context:
space:
mode:
authorSimon Rettberg2018-10-16 10:08:48 +0200
committerSimon Rettberg2018-10-16 10:08:48 +0200
commitd3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch)
treecbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/piecewise.c
downloadxscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip
Original 5.40
Diffstat (limited to 'hacks/piecewise.c')
-rw-r--r--hacks/piecewise.c1006
1 files changed, 1006 insertions, 0 deletions
diff --git a/hacks/piecewise.c b/hacks/piecewise.c
new file mode 100644
index 0000000..6e6e484
--- /dev/null
+++ b/hacks/piecewise.c
@@ -0,0 +1,1006 @@
+/* piecewise, 21jan2003
+ * Geoffrey Irving <irving@caltech.edu>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include <stdarg.h>
+#include <math.h>
+#include "screenhack.h"
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+# include "xdbe.h"
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+#if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
+#undef inline
+#define inline /* */
+#endif
+
+#define X_PI (180 * 64)
+
+#define START 0
+#define CROSS 1
+#define FINISH 2
+
+#define ARC_BUFFER_SIZE 256
+
+
+typedef struct _tree {
+ struct _tree *l, *r; /* left and right children */
+ /* extra stuff would go here */
+ } tree;
+
+
+struct _fringe;
+
+typedef struct _circle {
+ int r; /* radius */
+ double x, y; /* position */
+ double dx, dy; /* velocity */
+
+ int visible; /* default visibility */
+ struct _fringe *lo, *hi; /* lo and hi fringes */
+
+ int ni; /* number of intersections */
+ int *i; /* sorted intersection list */
+ } circle;
+
+typedef struct _fringe {
+ struct _fringe *l, *r; /* left and right children for splay trees */
+
+ circle *c; /* associated circle */
+ int side; /* 0 for lo, 1 for hi */
+
+ int mni; /* size of intersection array */
+ int ni; /* number of intersections */
+ int *i; /* sorted intersection list */
+ } fringe;
+
+
+typedef struct _event {
+ struct _event *l, *r; /* left and right children for splay tree */
+
+ int kind; /* type of event */
+ double x, y; /* position */
+ fringe *lo, *hi; /* fringes */
+ } event;
+
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ double event_cut_y;
+
+ double fringe_start_cut_x;
+ double fringe_start_cut_y;
+
+ double fringe_double_cut_x;
+ double fringe_double_cut_y;
+ fringe *fringe_double_cut_lo;
+ fringe *fringe_double_cut_hi;
+
+ int arc_buffer_count;
+ XArc arc_buffer[ARC_BUFFER_SIZE];
+
+ Bool dbuf;
+ XColor *colors;
+ XGCValues gcv;
+ GC erase_gc, draw_gc;
+ XWindowAttributes xgwa;
+ Pixmap b, ba, bb; /* double-buffering pixmap */
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ XdbeBackBuffer backb;
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+ int count, delay, ncolors, colorspeed, color_index, flags, iterations;
+ int color_iterations;
+ circle *circles;
+};
+
+typedef int (*cut)(struct state *, tree*); /* cut x is <, =, or > 0 given a <, =, or > x for some a */
+
+
+
+/******** splaying code */
+
+/* Top-down splay routine. Reference:
+ * "Self-adjusting Binary Search Trees", Sleator and Tarjan,
+ * JACM Volume 32, No 3, July 1985, pp 652-686.
+ * See page 668 for specific splay transformations */
+
+static tree *splay(struct state *st, cut c, tree *t)
+{
+ int v, vv;
+ tree *l, *r;
+ tree **lr, **rl;
+ tree *x, *y, *z;
+
+ if (!t)
+ return 0;
+
+ /* initialization */
+ x = t;
+ l = r = 0;
+ lr = &l;
+ rl = &r;
+
+ /* top-down splaying loop */
+ for (;;) {
+ v = c(st, x);
+ if (v == 0)
+ break; /*** success ***/
+ else if (v < 0) {
+ y = x->l;
+ if (!y)
+ break; /*** trivial ***/
+ else {
+ vv = c(st, y);
+ if (vv == 0) {
+ *rl = x; /*** zig ***/
+ rl = &x->l;
+ x = y;
+ break;
+ }
+ else if (vv < 0) {
+ z = y->l;
+ if (!z) {
+ *rl = x; /*** zig ***/
+ rl = &x->l;
+ x = y;
+ break;
+ }
+ else {
+ x->l = y->r; /*** zig-zig ***/
+ y->r = x;
+ *rl = y;
+ rl = &y->l;
+ x = z;
+ }
+ }
+ else { /* vv > 0 */
+ z = y->r;
+ if (!z) {
+ *rl = x; /*** zig ***/
+ rl = &x->l;
+ x = y;
+ break;
+ }
+ else { /*** zig-zag ***/
+ *lr = y;
+ lr = &y->r;
+ *rl = x;
+ rl = &x->l;
+ x = z;
+ }
+ }
+ }
+ }
+ else { /* v > 0 */
+ y = x->r;
+ if (!y)
+ break; /*** trivial ***/
+ else {
+ vv = c(st, y);
+ if (vv == 0) {
+ *lr = x; /*** zig ***/
+ lr = &x->r;
+ x = y;
+ break;
+ }
+ else if (vv > 0) {
+ z = y->r;
+ if (!z) {
+ *lr = x; /*** zig ***/
+ lr = &x->r;
+ x = y;
+ break;
+ }
+ else {
+ x->r = y->l; /*** zig-zig ***/
+ y->l = x;
+ *lr = y;
+ lr = &y->r;
+ x = z;
+ }
+ }
+ else { /* vv < 0 */
+ z = y->l;
+ if (!z) {
+ *lr = x; /*** zig ***/
+ lr = &x->r;
+ x = y;
+ break;
+ }
+ else { /*** zig-zag ***/
+ *rl = y;
+ rl = &y->l;
+ *lr = x;
+ lr = &x->r;
+ x = z;
+ }
+ }
+ }
+ }
+ }
+
+ /* completion */
+ *lr = x->l;
+ x->l = l;
+ *rl = x->r;
+ x->r = r;
+ return x;
+ }
+
+static tree *splay_min(tree *t)
+{
+ tree *r, **rl;
+ tree *x, *y, *z;
+
+ if (!t)
+ return 0;
+
+ x = t;
+ r = 0;
+ rl = &r;
+
+ for (;;) {
+ y = x->l;
+ if (!y)
+ break; /*** trivial ***/
+ else {
+ z = y->l;
+ if (!z) {
+ *rl = x; /*** zig ***/
+ rl = &x->l;
+ x = y;
+ break;
+ }
+ else {
+ x->l = y->r; /*** zig-zig ***/
+ y->r = x;
+ *rl = y;
+ rl = &y->l;
+ x = z;
+ }
+ }
+ }
+
+ x->l = 0;
+ *rl = x->r;
+ x->r = r;
+ return x;
+ }
+
+static tree *splay_max(tree *t)
+{
+ tree *l, **lr;
+ tree *x, *y, *z;
+
+ if (!t)
+ return 0;
+
+ x = t;
+ l = 0;
+ lr = &l;
+
+ for (;;) {
+ y = x->r;
+ if (!y)
+ break; /*** trivial ***/
+ else {
+ z = y->r;
+ if (!z) {
+ *lr = x; /*** zig ***/
+ lr = &x->r;
+ x = y;
+ break;
+ }
+ else {
+ x->r = y->l; /*** zig-zig ***/
+ y->l = x;
+ *lr = y;
+ lr = &y->r;
+ x = z;
+ }
+ }
+ }
+
+ *lr = x->l;
+ x->l = l;
+ x->r = 0;
+ return x;
+ }
+
+/******** circles and fringe */
+
+static inline double fringe_x(fringe *f, double y)
+{
+ double dy, d;
+ dy = f->c->y - y;
+ d = sqrt(f->c->r * f->c->r - dy * dy);
+ return f->side ? f->c->x + d : f->c->x - d;
+ }
+
+static inline void fringe_add_intersection(fringe *f, double x, double y)
+{
+ f->ni++;
+ if (f->mni < f->ni) {
+ f->mni += 2;
+ f->i = realloc(f->i, sizeof(int) * f->mni);
+ }
+ f->i[f->ni-1] = rint(atan2(y - f->c->y, x - f->c->x) * X_PI / M_PI);
+ }
+
+static circle *init_circles(struct state *st, int n, int w, int h)
+{
+ int i, r0, dr, speed;
+ double v, a;
+ double minradius, maxradius;
+ fringe *s = malloc(sizeof(fringe) * n * 2); /* never freed */
+ circle *c = malloc(sizeof(circle) * n);
+
+ speed = get_integer_resource(st->dpy, "speed", "Speed");
+ minradius = get_float_resource(st->dpy, "minradius", "Float");
+ maxradius = get_float_resource(st->dpy, "maxradius", "Float");
+ if (maxradius < minradius)
+ maxradius = minradius;
+
+ r0 = ceil(minradius * h);
+ dr = floor(maxradius * h) - r0 + 1;
+
+ for (i=0;i<n;i++) {
+ c[i].r = r0 + ((dr > 0) ? random() % dr : 0);
+ c[i].x = c[i].r + frand(w - 1 - 2 * c[i].r);
+ c[i].y = c[i].r + frand(h - 1 - 2 * c[i].r);
+ c[i].visible = random() & 1;
+
+ c[i].ni = 0;
+ c[i].i = 0;
+
+ a = frand(2 * M_PI);
+ v = (1 + frand(0.5)) * speed / 10.0;
+ c[i].dx = v * cos(a);
+ c[i].dy = v * sin(a);
+
+ c[i].lo = s+i+i;
+ c[i].hi = s+i+i+1;
+ c[i].lo->c = c[i].hi->c = c+i;
+ c[i].lo->side = 0;
+ c[i].hi->side = 1;
+ c[i].lo->mni = c[i].lo->ni = c[i].hi->mni = c[i].hi->ni = 0;
+ c[i].lo->i = c[i].hi->i = 0;
+ }
+
+ return c;
+ }
+
+/* this is a hack, but I guess that's what I writing anyways */
+static void tweak_circle(circle *c)
+{
+ c->x += frand(2) - 1;
+ c->y += frand(1) + 0.1;
+ }
+
+static void move_circle(circle *c, int w, int h)
+{
+ c->x += c->dx;
+ if (c->x < c->r) {
+ c->x = c->r;
+ c->dx = -c->dx;
+ }
+ else if (c->x >= w - c->r) {
+ c->x = w - 1 - c->r;
+ c->dx = -c->dx;
+ }
+ c->y += c->dy;
+ if (c->y < c->r) {
+ c->y = c->r;
+ c->dy = -c->dy;
+ }
+ else if (c->y >= h - c->r) {
+ c->y = h - 1 - c->r;
+ c->dy = -c->dy;
+ }
+ }
+
+/******** event queue */
+
+static int event_cut(struct state *st, event *e)
+{
+ return st->event_cut_y == e->y ? 0 : st->event_cut_y < e->y ? -1 : 1;
+ }
+
+static void event_insert(struct state *st, event **eq, event *e)
+{
+ if (!*eq) {
+ e->l = e->r = 0;
+ *eq = e;
+ return; /* avoid leak */
+ }
+
+ st->event_cut_y = e->y;
+ *eq = (event*)splay(st, (cut)event_cut, (tree*)*eq);
+
+ if (e->y == (*eq)->y) {
+ if (!((e->lo == (*eq)->lo && e->hi == (*eq)->hi) || (e->lo == (*eq)->hi && e->hi == (*eq)->lo))) {
+ e->l = (*eq)->l;
+ e->r = 0; /* doing this instead of dying might be dangerous */
+ (*eq)->l = e;
+ }
+ else
+ free(e); /* don't leak! */
+ }
+ else if (e->y < (*eq)->y) {
+ e->l = (*eq)->l;
+ e->r = *eq;
+ (*eq)->l = 0;
+ *eq = e;
+ }
+ else {
+ e->l = *eq;
+ e->r = (*eq)->r;
+ (*eq)->r = 0;
+ *eq = e;
+ }
+ }
+
+static void circle_start_event(struct state *st, event **eq, circle *c)
+{
+ event *s;
+ s = malloc(sizeof(event));
+ s->kind = START;
+ s->x = c->x;
+ s->y = c->y - c->r;
+ s->lo = c->lo;
+ s->hi = c->hi;
+ event_insert(st, eq, s);
+ }
+
+static void circle_finish_event(struct state *st, event **eq, circle *c)
+{
+ event *f;
+ f = malloc(sizeof(event));
+ f->kind = FINISH;
+ f->x = c->x;
+ f->y = c->y + c->r;
+ f->lo = c->lo;
+ f->hi = c->hi;
+ event_insert(st, eq, f);
+ }
+
+static event *event_next(event **eq)
+{
+ event *e;
+ if (!*eq)
+ return 0;
+ else {
+ e = (event*)splay_min((tree*)*eq);
+ *eq = e->r;
+ return e;
+ }
+ }
+
+static void event_shred(event *e)
+{
+ if (e) {
+ event_shred(e->l);
+ event_shred(e->r);
+ free(e);
+ }
+ }
+
+/******** fringe intersection */
+
+#if 0
+static inline int check_fringe_intersection(double ye, fringe *lo, fringe *hi, double x, double y)
+{
+ return ye <= y && ((x < lo->c->x) ^ lo->side) && ((x < hi->c->x) ^ hi->side);
+ }
+#endif /* 0 */
+
+static void fringe_intersect(struct state *st, event **eq, double y, fringe *lo, fringe *hi)
+{
+ event *e;
+ double dx, dy, sd, rs, rd, d, sx, sy, rp, sqd;
+ double x1, y1, x2, y2;
+
+ if (lo->c == hi->c)
+ return;
+
+ dx = hi->c->x - lo->c->x;
+ dy = hi->c->y - lo->c->y;
+ sd = dx * dx + dy * dy;
+
+ if (sd == 0)
+ return;
+
+ rs = hi->c->r + lo->c->r;
+ rd = hi->c->r - lo->c->r;
+ d = (rd * rd - sd) * (sd - rs * rs);
+
+ if (d <= 0)
+ return;
+
+ sd = 0.5 / sd;
+ rp = rs * rd;
+ sqd = sqrt(d);
+ sx = (lo->c->x + hi->c->x) / 2;
+ sy = (lo->c->y + hi->c->y) / 2;
+ x1 = sx + sd * (dy * sqd - dx * rp);
+ y1 = sy - sd * (dx * sqd + dy * rp);
+ x2 = sx - sd * (dy * sqd + dx * rp);
+ y2 = sy + sd * (dx * sqd - dy * rp);
+
+ #define CHECK(xi, yi) (y <= yi && ((xi < lo->c->x) ^ lo->side) && ((xi < hi->c->x) ^ hi->side))
+
+ #define ADD_CROSS(xi, yi, ilo, ihi) { \
+ e = malloc(sizeof(event)); /* #### LEAK */ \
+ e->kind = CROSS; \
+ e->x = xi; e->y = yi; \
+ e->lo = ilo; e->hi = ihi; \
+ event_insert(st, eq, e); \
+ }
+
+ if (CHECK(x1, y1)) {
+ if (CHECK(x2, y2)) {
+ if (y1 < y2) {
+ ADD_CROSS(x1, y1, lo, hi);
+ ADD_CROSS(x2, y2, hi, lo);
+ }
+ else {
+ ADD_CROSS(x1, y1, hi, lo);
+ ADD_CROSS(x2, y2, lo, hi);
+ }
+ }
+ else
+ ADD_CROSS(x1, y1, lo, hi);
+ }
+ else if (CHECK(x2, y2))
+ ADD_CROSS(x2, y2, lo, hi);
+
+ return;
+ }
+
+/******** fringe trees and event handling */
+
+#define PANIC ((fringe*)1) /* by alignment, no fringe should every be 1 */
+
+static fringe *check_lo(struct state *st, event **eq, double y, fringe *f, fringe *hi)
+{
+ if (f) {
+ f = (fringe*)splay_max((tree*)f);
+ fringe_intersect(st, eq, y, f, hi);
+ }
+ return f;
+ }
+
+static fringe *check_hi(struct state *st, event **eq, double y, fringe *lo, fringe *f)
+{
+ if (f) {
+ f = (fringe*)splay_min((tree*)f);
+ fringe_intersect(st, eq, y, lo, f);
+ }
+ return f;
+ }
+
+static int fringe_start_cut(struct state *st, fringe *f)
+{
+ double x = fringe_x(f, st->fringe_start_cut_y);
+ return st->fringe_start_cut_x == x ? 0 : st->fringe_start_cut_x < x ? -1 : 1;
+ }
+
+static fringe *fringe_start(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
+{
+ double sx;
+
+ if (!f) {
+ circle_finish_event(st, eq, lo->c);
+ lo->l = 0;
+ lo->r = hi;
+ hi->l = hi->r = 0;
+ return lo;
+ }
+
+ st->fringe_start_cut_x = x;
+ st->fringe_start_cut_y = y;
+ f = (fringe*)splay(st, (cut)fringe_start_cut, (tree*)f);
+
+ sx = fringe_x(f, y);
+ if (x == sx) { /* time to cheat my way out of handling degeneracies */
+ tweak_circle(lo->c);
+ circle_start_event(st, eq, lo->c);
+ return f;
+ }
+ else if (x < sx) {
+ circle_finish_event(st, eq, lo->c);
+ f->l = check_lo(st, eq, y, f->l, lo);
+ fringe_intersect(st, eq, y, hi, f);
+ lo->l = f->l;
+ lo->r = f;
+ f->l = hi;
+ hi->l = hi->r = 0;
+ return lo;
+ }
+ else {
+ circle_finish_event(st, eq, lo->c);
+ fringe_intersect(st, eq, y, f, lo);
+ f->r = check_hi(st, eq, y, hi, f->r);
+ hi->r = f->r;
+ hi->l = f;
+ f->r = lo;
+ lo->l = lo->r = 0;
+ return hi;
+ }
+ }
+
+static int fringe_double_cut(struct state *st, fringe *f)
+{
+ double x;
+ if (f == st->fringe_double_cut_lo || f == st->fringe_double_cut_hi)
+ return 0;
+ x = fringe_x(f, st->fringe_double_cut_y);
+ return st->fringe_double_cut_x == x ? 0 : st->fringe_double_cut_x < x ? -1 : 1;
+ }
+
+static int fringe_double_splay(struct state *st, fringe *f, double x, double y, fringe *lo, fringe *hi)
+{
+ st->fringe_double_cut_x = x;
+ st->fringe_double_cut_y = y;
+ st->fringe_double_cut_lo = lo;
+ st->fringe_double_cut_hi = hi;
+ f = (fringe*)splay(st, (cut)fringe_double_cut, (tree*)f);
+
+ if (f == lo)
+ return (f->r = (fringe*)splay_min((tree*)f->r)) == hi;
+ else if (f == hi)
+ return (f->l = (fringe*)splay_max((tree*)f->l)) == lo;
+ else
+ return 0;
+ }
+
+static fringe *fringe_cross(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
+{
+ fringe *l, *r;
+ if (!fringe_double_splay(st, f, x, y, lo, hi))
+ return PANIC;
+ l = check_lo(st, eq, y, lo->l, hi);
+ r = check_hi(st, eq, y, lo, hi->r);
+ lo->l = hi;
+ lo->r = r;
+ hi->l = l;
+ hi->r = 0;
+ return lo;
+ }
+
+static fringe *fringe_finish(struct state *st, event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi)
+{
+ if (!fringe_double_splay(st, f, x, y, lo, hi))
+ return PANIC;
+ else if (!lo->l)
+ return hi->r;
+ else if (!hi->r)
+ return lo->l;
+ else {
+ lo->l = (fringe*)splay_max((tree*)lo->l);
+ hi->r = (fringe*)splay_min((tree*)hi->r);
+ fringe_intersect(st, eq, y, lo->l, hi->r);
+ lo->l->r = hi->r;
+ hi->r->l = 0;
+ return lo->l;
+ }
+ }
+
+/******** plane sweep */
+
+static void sweep(struct state *st, int n, circle *c)
+{
+ int i;
+ event *eq, *e;
+ fringe *f;
+
+ RESTART:
+ #define CHECK_PANIC() \
+ if (f == PANIC) { \
+ free(e); \
+ event_shred(eq); \
+ for (i=0;i<n;i++) { \
+ tweak_circle(c+i); \
+ c[i].lo->ni = c[i].hi->ni = 0; \
+ } \
+ goto RESTART; \
+ }
+
+ eq = 0;
+ for (i=0;i<n;i++)
+ circle_start_event(st, &eq, c+i);
+ f = 0;
+
+ while ((e = event_next(&eq))) {
+ switch (e->kind) {
+ case START:
+ f = fringe_start(st, &eq, f, e->x, e->y, e->lo, e->hi);
+ break;
+ case CROSS:
+ f = fringe_cross(st, &eq, f, e->x, e->y, e->lo, e->hi);
+ CHECK_PANIC();
+ fringe_add_intersection(e->lo, e->x, e->y);
+ fringe_add_intersection(e->hi, e->x, e->y);
+ break;
+ case FINISH:
+ f = fringe_finish(st, &eq, f, e->x, e->y, e->lo, e->hi);
+ CHECK_PANIC();
+ break;
+ }
+ free(e);
+ }
+ }
+
+/******** circle drawing */
+
+static void adjust_circle_visibility(circle *c)
+{
+ int i, j, n, a;
+ int *in;
+ n = c->lo->ni + c->hi->ni;
+ in = malloc(sizeof(int) * n);
+ for (i=0;i<c->hi->ni;i++)
+ in[i] = c->hi->i[i];
+ for (i=c->lo->ni-1;i>=0;i--)
+ in[n-i-1] = c->lo->i[i] > 0 ? c->lo->i[i] : c->lo->i[i] + 2 * X_PI;
+ c->lo->ni = c->hi->ni = 0;
+
+ i = j = 0;
+ a = 0;
+ while (i < n && j < c->ni) /* whee */
+ a = (in[i] < c->i[j] ? in[i++] : c->i[j++]) - a;
+ while (i < n)
+ a = in[i++] - a;
+ while (j < c->ni)
+ a = c->i[j++] - a;
+
+ if (a > X_PI)
+ c->visible = !c->visible;
+ free(c->i);
+ c->ni = n;
+ c->i = in;
+ }
+
+static void flush_arc_buffer(struct state *st, Drawable w, GC gc)
+{
+ if (st->arc_buffer_count) {
+ XDrawArcs(st->dpy, w, gc, st->arc_buffer, st->arc_buffer_count);
+ st->arc_buffer_count = 0;
+ }
+ }
+
+static void draw_circle(struct state *st, Drawable w, GC gc, circle *c)
+{
+ int i, xi, yi, di;
+ adjust_circle_visibility(c);
+
+ xi = rint(c->x - c->r);
+ yi = rint(c->y - c->r);
+ di = c->r << 1;
+
+ #define ARC(p, a1, a2) { \
+ if (((p) & 1) ^ c->visible) { \
+ st->arc_buffer[st->arc_buffer_count].x = xi; \
+ st->arc_buffer[st->arc_buffer_count].y = yi; \
+ st->arc_buffer[st->arc_buffer_count].width = di; \
+ st->arc_buffer[st->arc_buffer_count].height = di; \
+ st->arc_buffer[st->arc_buffer_count].angle1 = -(a1); \
+ st->arc_buffer[st->arc_buffer_count].angle2 = (a1) - (a2); \
+ st->arc_buffer_count++; \
+ if (st->arc_buffer_count == ARC_BUFFER_SIZE) \
+ flush_arc_buffer(st, w, gc); \
+ } \
+ }
+
+ if (!c->ni)
+ ARC(0, 0, 2 * X_PI)
+ else
+ ARC(0, c->i[c->ni-1], c->i[0] + 2 * X_PI)
+ for (i=1;i<c->ni;i++)
+ ARC(i, c->i[i-1], c->i[i])
+ }
+
+/******** toplevel */
+
+#if 0
+static void
+check_for_leaks (void)
+{
+#ifdef HAVE_SBRK
+ static unsigned long early_brk = 0;
+ unsigned long max = 30 * 1024 * 1024; /* 30 MB */
+ int b = (unsigned long) sbrk(0);
+ if (early_brk == 0)
+ early_brk = b;
+ else if (b > early_brk + max)
+ {
+ fprintf (stderr, "%s: leaked %lu MB -- aborting!\n",
+ progname, ((b - early_brk) >> 20));
+ exit (1);
+ }
+#endif /* HAVE_SBRK */
+}
+#endif
+
+static void *
+piecewise_init (Display *dd, Window ww)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ st->dpy = dd;
+ st->window = ww;
+
+ st->count = get_integer_resource(st->dpy, "count", "Integer");
+ st->delay = get_integer_resource(st->dpy, "delay", "Integer");
+ st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
+ st->colorspeed = get_integer_resource(st->dpy, "colorspeed", "Integer");
+ st->dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean");
+
+# ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
+ st->dbuf = False;
+# endif
+
+ st->color_iterations = st->colorspeed ? 100 / st->colorspeed : 100000;
+ if (!st->color_iterations)
+ st->color_iterations = 1;
+
+ XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
+ st->colors = calloc(sizeof(XColor), st->ncolors);
+
+ if (get_boolean_resource(st->dpy, "mono", "Boolean")) {
+ MONO:
+ st->ncolors = 1;
+ st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap, "foreground", "Foreground");
+ }
+ else {
+ make_color_loop(st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
+ 0, 1, 1, 120, 1, 1, 240, 1, 1,
+ st->colors, &st->ncolors, True, False);
+ if (st->ncolors < 2)
+ goto MONO;
+ }
+
+ if (st->dbuf) {
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ st->b = st->backb = xdbe_get_backbuffer(st->dpy, st->window, XdbeUndefined);
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+ if (!st->b) {
+ st->ba = XCreatePixmap(st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
+ st->bb = XCreatePixmap(st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
+ st->b = st->ba;
+ }
+ }
+ else
+ st->b = st->window;
+
+ /* erasure gc */
+ st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, "background", "Background");
+ st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &st->gcv);
+
+ /* drawing gc */
+ st->flags = GCForeground;
+ st->color_index = random() % st->ncolors;
+ st->gcv.foreground = st->colors[st->color_index].pixel;
+ st->draw_gc = XCreateGC(st->dpy, st->b, st->flags, &st->gcv);
+
+ /* initialize circles */
+ st->circles = init_circles(st, st->count, st->xgwa.width, st->xgwa.height);
+
+ st->iterations = 0;
+
+ return st;
+}
+
+static unsigned long
+piecewise_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ int i;
+
+ XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
+
+ sweep(st, st->count, st->circles);
+ for (i=0;i<st->count;i++) {
+ draw_circle(st, st->b, st->draw_gc, st->circles+i);
+ move_circle(st->circles+i, st->xgwa.width, st->xgwa.height);
+ }
+ flush_arc_buffer(st, st->b, st->draw_gc);
+
+ if (++st->iterations % st->color_iterations == 0) {
+ st->color_index = (st->color_index + 1) % st->ncolors;
+ XSetForeground(st->dpy, st->draw_gc, st->colors[st->color_index].pixel);
+ }
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ if (st->backb) {
+ XdbeSwapInfo info[1];
+ info[0].swap_window = st->window;
+ info[0].swap_action = XdbeUndefined;
+ XdbeSwapBuffers (st->dpy, info, 1);
+ }
+ else
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+ if (st->dbuf) {
+ XCopyArea (st->dpy, st->b, st->window, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height, 0, 0);
+ st->b = (st->b == st->ba ? st->bb : st->ba);
+ }
+
+/* check_for_leaks(); */
+ return st->delay;
+}
+
+static void
+piecewise_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct state *st = (struct state *) closure;
+ XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
+}
+
+static Bool
+piecewise_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+piecewise_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
+
+
+
+static const char *piecewise_defaults [] = {
+ ".background: black",
+ ".foreground: white",
+ "*delay: 10000",
+ "*speed: 15",
+ "*ncolors: 256",
+ ".colorspeed: 10",
+
+ ".count: 32",
+ ".minradius: 0.05",
+ ".maxradius: 0.2",
+
+ "*doubleBuffer: True",
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ "*useDBE: True",
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+#endif
+ 0
+ };
+
+static XrmOptionDescRec piecewise_options [] = {
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-colorspeed", ".colorspeed", XrmoptionSepArg, 0 },
+
+ { "-count", ".count", XrmoptionSepArg, 0 },
+ { "-minradius", ".minradius", XrmoptionSepArg, 0 },
+ { "-maxradius", ".maxradius", XrmoptionSepArg, 0 },
+
+ { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
+ { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
+ { 0, 0, 0, 0 }
+ };
+
+
+XSCREENSAVER_MODULE ("Piecewise", piecewise)