1 /* piecewise, 21jan2003
2 * Geoffrey Irving <irving@caltech.edu>
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
15 #include "screenhack.h"
17 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
19 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
21 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
26 #define X_PI (180 * 64)
28 /******** splaying code */
30 typedef struct _tree {
31 struct _tree *l, *r; /* left and right children */
32 /* extra stuff would go here */
35 typedef int (*cut)(tree*); /* cut x is <, =, or > 0 given a <, =, or > x for some a */
37 /* Top-down splay routine. Reference:
38 * "Self-adjusting Binary Search Trees", Sleator and Tarjan,
39 * JACM Volume 32, No 3, July 1985, pp 652-686.
40 * See page 668 for specific splay transformations */
42 tree *splay(cut c, tree *t) {
57 /* top-down splaying loop */
61 break; /*** success ***/
65 break; /*** trivial ***/
69 *rl = x; /*** zig ***/
77 *rl = x; /*** zig ***/
83 x->l = y->r; /*** zig-zig ***/
93 *rl = x; /*** zig ***/
98 else { /*** zig-zag ***/
111 break; /*** trivial ***/
115 *lr = x; /*** zig ***/
123 *lr = x; /*** zig ***/
129 x->r = y->l; /*** zig-zig ***/
139 *lr = x; /*** zig ***/
144 else { /*** zig-zag ***/
164 tree *splay_min(tree *t) {
178 break; /*** trivial ***/
182 *rl = x; /*** zig ***/
188 x->l = y->r; /*** zig-zig ***/
203 tree *splay_max(tree *t) {
217 break; /*** trivial ***/
221 *lr = x; /*** zig ***/
227 x->r = y->l; /*** zig-zig ***/
242 /******** circles and fringe */
246 typedef struct _circle {
248 double x, y; /* position */
249 double dx, dy; /* velocity */
251 int visible; /* default visibility */
252 struct _fringe *lo, *hi; /* lo and hi fringes */
254 int ni; /* number of intersections */
255 int *i; /* sorted intersection list */
258 typedef struct _fringe {
259 struct _fringe *l, *r; /* left and right children for splay trees */
261 circle *c; /* associated circle */
262 int side; /* 0 for lo, 1 for hi */
264 int mni; /* size of intersection array */
265 int ni; /* number of intersections */
266 int *i; /* sorted intersection list */
269 inline double fringe_x(fringe *f, double y) {
272 d = sqrt(f->c->r * f->c->r - dy * dy);
273 return f->side ? f->c->x + d : f->c->x - d;
276 inline void fringe_add_intersection(fringe *f, double x, double y) {
278 if (f->mni < f->ni) {
280 f->i = realloc(f->i, sizeof(int) * f->mni);
282 f->i[f->ni-1] = rint(atan2(y - f->c->y, x - f->c->x) * X_PI / M_PI);
285 circle *init_circles(int n, int w, int h) {
286 int i, r0, dr, speed;
288 double minradius, maxradius;
289 fringe *s = malloc(sizeof(fringe) * n * 2); /* never freed */
290 circle *c = malloc(sizeof(circle) * n);
292 speed = get_integer_resource("speed", "Speed");
293 minradius = get_float_resource("minradius", "Float");
294 maxradius = get_float_resource("maxradius", "Float");
295 if (maxradius < minradius)
296 maxradius = minradius;
298 r0 = ceil(minradius * h);
299 dr = floor(maxradius * h) - r0 + 1;
302 c[i].r = r0 + random() % dr;
303 c[i].x = c[i].r + frand(w - 1 - 2 * c[i].r);
304 c[i].y = c[i].r + frand(h - 1 - 2 * c[i].r);
305 c[i].visible = random() & 1;
311 v = (1 + frand(0.5)) * speed / 10.0;
312 c[i].dx = v * cos(a);
313 c[i].dy = v * sin(a);
317 c[i].lo->c = c[i].hi->c = c+i;
320 c[i].lo->mni = c[i].lo->ni = c[i].hi->mni = c[i].hi->ni = 0;
321 c[i].lo->i = c[i].hi->i = 0;
327 /* this is a hack, but I guess that's what I writing anyways */
328 void tweak_circle(circle *c) {
329 c->x += frand(2) - 1;
330 c->y += frand(1) + 0.1;
333 void move_circle(circle *c, int w, int h) {
339 else if (c->x >= w - c->r) {
348 else if (c->y >= h - c->r) {
354 /******** event queue */
360 typedef struct _event {
361 struct _event *l, *r; /* left and right children for splay tree */
363 int kind; /* type of event */
364 double x, y; /* position */
365 fringe *lo, *hi; /* fringes */
368 static double event_cut_y;
370 int event_cut(event *e) {
371 return event_cut_y == e->y ? 0 : event_cut_y < e->y ? -1 : 1;
374 void event_insert(event **eq, event *e) {
378 return; /* avoid leak */
382 *eq = (event*)splay((cut)event_cut, (tree*)*eq);
384 if (e->y == (*eq)->y) {
385 if (!((e->lo == (*eq)->lo && e->hi == (*eq)->hi) || (e->lo == (*eq)->hi && e->hi == (*eq)->lo))) {
387 e->r = 0; /* doing this instead of dying might be dangerous */
391 free(e); /* don't leak! */
393 else if (e->y < (*eq)->y) {
407 void circle_start_event(event **eq, circle *c) {
409 s = malloc(sizeof(event));
418 void circle_finish_event(event **eq, circle *c) {
420 f = malloc(sizeof(event));
429 event *event_next(event **eq) {
434 e = (event*)splay_min((tree*)*eq);
440 void event_shred(event *e) {
448 /******** fringe intersection */
450 inline int check_fringe_intersection(double ye, fringe *lo, fringe *hi, double x, double y) {
451 return ye <= y && ((x < lo->c->x) ^ lo->side) && ((x < hi->c->x) ^ hi->side);
454 void fringe_intersect(event **eq, double y, fringe *lo, fringe *hi) {
456 double dx, dy, sd, rs, rd, d, sx, sy, rp, sqd;
457 double x1, y1, x2, y2;
462 dx = hi->c->x - lo->c->x;
463 dy = hi->c->y - lo->c->y;
464 sd = dx * dx + dy * dy;
469 rs = hi->c->r + lo->c->r;
470 rd = hi->c->r - lo->c->r;
471 d = (rd * rd - sd) * (sd - rs * rs);
479 sx = (lo->c->x + hi->c->x) / 2;
480 sy = (lo->c->y + hi->c->y) / 2;
481 x1 = sx + sd * (dy * sqd - dx * rp);
482 y1 = sy - sd * (dx * sqd + dy * rp);
483 x2 = sx - sd * (dy * sqd + dx * rp);
484 y2 = sy + sd * (dx * sqd - dy * rp);
486 #define CHECK(xi, yi) (y <= yi && ((xi < lo->c->x) ^ lo->side) && ((xi < hi->c->x) ^ hi->side))
488 #define ADD_CROSS(xi, yi, ilo, ihi) { \
489 e = malloc(sizeof(event)); /* #### LEAK */ \
491 e->x = xi; e->y = yi; \
492 e->lo = ilo; e->hi = ihi; \
493 event_insert(eq, e); \
499 ADD_CROSS(x1, y1, lo, hi);
500 ADD_CROSS(x2, y2, hi, lo);
503 ADD_CROSS(x1, y1, hi, lo);
504 ADD_CROSS(x2, y2, lo, hi);
508 ADD_CROSS(x1, y1, lo, hi);
510 else if (CHECK(x2, y2))
511 ADD_CROSS(x2, y2, lo, hi);
516 /******** fringe trees and event handling */
518 #define PANIC ((fringe*)1) /* by alignment, no fringe should every be 1 */
520 fringe *check_lo(event **eq, double y, fringe *f, fringe *hi) {
522 f = (fringe*)splay_max((tree*)f);
523 fringe_intersect(eq, y, f, hi);
528 fringe *check_hi(event **eq, double y, fringe *lo, fringe *f) {
530 f = (fringe*)splay_min((tree*)f);
531 fringe_intersect(eq, y, lo, f);
536 double fringe_start_cut_x;
537 double fringe_start_cut_y;
539 int fringe_start_cut(fringe *f) {
540 double x = fringe_x(f, fringe_start_cut_y);
541 return fringe_start_cut_x == x ? 0 : fringe_start_cut_x < x ? -1 : 1;
544 fringe *fringe_start(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
548 circle_finish_event(eq, lo->c);
555 fringe_start_cut_x = x;
556 fringe_start_cut_y = y;
557 f = (fringe*)splay((cut)fringe_start_cut, (tree*)f);
560 if (x == sx) { /* time to cheat my way out of handling degeneracies */
562 circle_start_event(eq, lo->c);
566 circle_finish_event(eq, lo->c);
567 f->l = check_lo(eq, y, f->l, lo);
568 fringe_intersect(eq, y, hi, f);
576 circle_finish_event(eq, lo->c);
577 fringe_intersect(eq, y, f, lo);
578 f->r = check_hi(eq, y, hi, f->r);
587 double fringe_double_cut_x;
588 double fringe_double_cut_y;
589 fringe *fringe_double_cut_lo;
590 fringe *fringe_double_cut_hi;
592 int fringe_double_cut(fringe *f) {
594 if (f == fringe_double_cut_lo || f == fringe_double_cut_hi)
596 x = fringe_x(f, fringe_double_cut_y);
597 return fringe_double_cut_x == x ? 0 : fringe_double_cut_x < x ? -1 : 1;
600 int fringe_double_splay(fringe *f, double x, double y, fringe *lo, fringe *hi) {
601 fringe_double_cut_x = x;
602 fringe_double_cut_y = y;
603 fringe_double_cut_lo = lo;
604 fringe_double_cut_hi = hi;
605 f = (fringe*)splay((cut)fringe_double_cut, (tree*)f);
608 return (f->r = (fringe*)splay_min((tree*)f->r)) == hi;
610 return (f->l = (fringe*)splay_max((tree*)f->l)) == lo;
615 fringe *fringe_cross(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
617 if (!fringe_double_splay(f, x, y, lo, hi))
619 l = check_lo(eq, y, lo->l, hi);
620 r = check_hi(eq, y, lo, hi->r);
628 fringe *fringe_finish(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
629 if (!fringe_double_splay(f, x, y, lo, hi))
636 lo->l = (fringe*)splay_max((tree*)lo->l);
637 hi->r = (fringe*)splay_min((tree*)hi->r);
638 fringe_intersect(eq, y, lo->l, hi->r);
645 /******** plane sweep */
647 void sweep(int n, circle *c) {
653 #define CHECK_PANIC() \
657 for (i=0;i<n;i++) { \
659 c[i].lo->ni = c[i].hi->ni = 0; \
666 circle_start_event(&eq, c+i);
669 while ((e = event_next(&eq))) {
672 f = fringe_start(&eq, f, e->x, e->y, e->lo, e->hi);
675 f = fringe_cross(&eq, f, e->x, e->y, e->lo, e->hi);
677 fringe_add_intersection(e->lo, e->x, e->y);
678 fringe_add_intersection(e->hi, e->x, e->y);
681 f = fringe_finish(&eq, f, e->x, e->y, e->lo, e->hi);
689 /******** circle drawing */
691 void adjust_circle_visibility(circle *c) {
694 n = c->lo->ni + c->hi->ni;
695 in = malloc(sizeof(int) * n);
696 for (i=0;i<c->hi->ni;i++)
698 for (i=c->lo->ni-1;i>=0;i--)
699 in[n-i-1] = c->lo->i[i] > 0 ? c->lo->i[i] : c->lo->i[i] + 2 * X_PI;
700 c->lo->ni = c->hi->ni = 0;
704 while (i < n && j < c->ni) /* whee */
705 a = (in[i] < c->i[j] ? in[i++] : c->i[j++]) - a;
712 c->visible = !c->visible;
718 #define ARC_BUFFER_SIZE 256
719 int arc_buffer_count = 0;
720 XArc arc_buffer[ARC_BUFFER_SIZE];
722 void flush_arc_buffer(Display *dpy, Drawable w, GC gc) {
723 if (arc_buffer_count) {
724 XDrawArcs(dpy, w, gc, arc_buffer, arc_buffer_count);
725 arc_buffer_count = 0;
729 void draw_circle(Display *dpy, Drawable w, GC gc, circle *c) {
731 adjust_circle_visibility(c);
733 xi = rint(c->x - c->r);
734 yi = rint(c->y - c->r);
737 #define ARC(p, a1, a2) { \
738 if (((p) & 1) ^ c->visible) { \
739 arc_buffer[arc_buffer_count].x = xi; \
740 arc_buffer[arc_buffer_count].y = yi; \
741 arc_buffer[arc_buffer_count].width = di; \
742 arc_buffer[arc_buffer_count].height = di; \
743 arc_buffer[arc_buffer_count].angle1 = -(a1); \
744 arc_buffer[arc_buffer_count].angle2 = (a1) - (a2); \
745 arc_buffer_count++; \
746 if (arc_buffer_count == ARC_BUFFER_SIZE) \
747 flush_arc_buffer(dpy, w, gc); \
754 ARC(0, c->i[c->ni-1], c->i[0] + 2 * X_PI)
755 for (i=1;i<c->ni;i++)
756 ARC(i, c->i[i-1], c->i[i])
759 /******** toplevel */
761 char *progclass = "Piecewise";
763 char *defaults [] = {
764 ".background: black",
765 ".foreground: white",
775 "*doubleBuffer: True",
776 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
778 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
782 XrmOptionDescRec options [] = {
783 { "-delay", ".delay", XrmoptionSepArg, 0 },
784 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
785 { "-speed", ".speed", XrmoptionSepArg, 0 },
786 { "-colorspeed", ".colorspeed", XrmoptionSepArg, 0 },
788 { "-count", ".count", XrmoptionSepArg, 0 },
789 { "-minradius", ".minradius", XrmoptionSepArg, 0 },
790 { "-maxradius", ".maxradius", XrmoptionSepArg, 0 },
792 { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
793 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
798 check_for_leaks (void)
801 static unsigned long early_brk = 0;
802 unsigned long max = 30 * 1024 * 1024; /* 30 MB */
803 int b = (unsigned long) sbrk(0);
806 else if (b > early_brk + max)
808 fprintf (stderr, "%s: leaked %lu MB -- aborting!\n",
809 progname, ((b - early_brk) >> 20));
812 #endif /* HAVE_SBRK */
816 void screenhack(Display *dpy, Window window) {
821 GC erase_gc, draw_gc;
822 XWindowAttributes xgwa;
823 Pixmap b = 0, ba = 0, bb = 0; /* double-buffering pixmap */
825 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
826 XdbeBackBuffer backb = 0;
827 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
829 int count, delay, ncolors, colorspeed, color_index, flags, iterations;
830 int color_iterations;
833 count = get_integer_resource("count", "Integer");
834 delay = get_integer_resource("delay", "Integer");
835 ncolors = get_integer_resource("ncolors", "Integer");
836 colorspeed = get_integer_resource("colorspeed", "Integer");
837 dbuf = get_boolean_resource("doubleBuffer", "Boolean");
839 color_iterations = colorspeed ? 100 / colorspeed : 100000;
840 if (!color_iterations)
841 color_iterations = 1;
843 XGetWindowAttributes(dpy, window, &xgwa);
844 colors = calloc(sizeof(XColor), ncolors);
846 if (get_boolean_resource("mono", "Boolean")) {
849 colors[0].pixel = get_pixel_resource("foreground", "Foreground", dpy, xgwa.colormap);
852 make_color_loop(dpy, xgwa.colormap, 0, 1, 1, 120, 1, 1, 240, 1, 1, colors, &ncolors, True, False);
858 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
859 b = backb = xdbe_get_backbuffer(dpy, window, XdbeUndefined);
860 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
863 ba = XCreatePixmap(dpy, window, xgwa.width, xgwa.height,xgwa.depth);
864 bb = XCreatePixmap(dpy, window, xgwa.width, xgwa.height,xgwa.depth);
872 gcv.foreground = get_pixel_resource("background", "Background", dpy, xgwa.colormap);
873 erase_gc = XCreateGC (dpy, b, GCForeground, &gcv);
876 flags = GCForeground;
877 color_index = random() % ncolors;
878 gcv.foreground = colors[color_index].pixel;
879 draw_gc = XCreateGC(dpy, b, flags, &gcv);
881 /* initialize circles */
882 circles = init_circles(count, xgwa.width, xgwa.height);
886 XFillRectangle (dpy, b, erase_gc, 0, 0, xgwa.width, xgwa.height);
888 sweep(count, circles);
889 for (i=0;i<count;i++) {
890 draw_circle(dpy, b, draw_gc, circles+i);
891 move_circle(circles+i, xgwa.width, xgwa.height);
893 flush_arc_buffer(dpy, b, draw_gc);
895 if (++iterations % color_iterations == 0) {
896 color_index = (color_index + 1) % ncolors;
897 XSetForeground(dpy, draw_gc, colors[color_index].pixel);
900 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
902 XdbeSwapInfo info[1];
903 info[0].swap_window = window;
904 info[0].swap_action = XdbeUndefined;
905 XdbeSwapBuffers (dpy, info, 1);
908 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
910 XCopyArea (dpy, b, window, erase_gc, 0, 0, xgwa.width, xgwa.height, 0, 0);
911 b = (b == ba ? bb : ba);
915 screenhack_handle_events(dpy);