ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.22.tar.gz
[xscreensaver] / hacks / intermomentary.c
1 /*
2  *  InterMomentary (dragorn@kismetwireless.net)
3  *  Directly ported code from complexification.net InterMomentary art
4  *  http://www.complexification.net/gallery/machines/interMomentary/applet_l/interMomentary_l.pde
5  *
6  * Intersecting Circles, Instantaneous
7  * J. Tarbell                              + complexification.net
8  * Albuquerque, New Mexico
9  * May, 2004
10  * 
11  * a REAS collaboration for the            + groupc.net
12  * Whitney Museum of American Art ARTPORT  + artport.whitney.org
13  * Robert Hodgin                           + flight404.com
14  * William Ngan                            + metaphorical.net
15  * 
16  *
17  * 1.0  Oct 10 2004  dragorn  Completed first port 
18  *
19  *
20  * Based, of course, on other hacks in:
21  *
22  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
23  *
24  * Permission to use, copy, modify, distribute, and sell this software and its
25  * documentation for any purpose is hereby granted without fee, provided that
26  * the above copyright notice appear in all copies and that both that
27  * copyright notice and this permission notice appear in supporting
28  * documentation.  No representations are made about the suitability of this
29  * software for any purpose.  It is provided "as is" without express or 
30  * implied warranty.
31  */
32
33 #include <math.h>
34 #include "screenhack.h"
35 #include <X11/Xutil.h>
36 #include <stdio.h>
37 #include <sys/time.h>
38
39 #ifndef MAX_WIDTH
40 #include <limits.h>
41 #define MAX_WIDTH SHRT_MAX
42 #endif
43
44 #ifdef TIME_ME
45 #include <time.h>
46 #endif
47
48 #include <math.h>
49
50 #include "hsv.h"
51
52 /* this program goes faster if some functions are inline.  The following is
53  * borrowed from ifs.c */
54 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
55 #undef inline
56 #define inline                  /* */
57 #endif
58
59 /* Pixel rider */
60 typedef struct {
61     float t;
62     float vt;
63     float mycharge;
64 } PxRider;
65
66 /* disc of light */
67 typedef struct {
68     /* index identifier */
69     int id;
70     /* position */
71     float x, y;
72     /* radius */
73     float r, dr;
74     /* velocity */
75     float vx, vy;
76
77     /* pixel riders */
78     int numr;
79     PxRider *pxRiders;
80 } Disc;
81
82 struct field {
83     unsigned int height;
84     unsigned int width;
85
86     int initial_discs;
87     Disc *discs;
88     
89     unsigned int num;
90
91     unsigned int maxrider;
92     unsigned int maxradius;
93
94     /* color parms */
95     unsigned long fgcolor;
96     unsigned long bgcolor;
97     int visdepth;
98
99     unsigned int cycles;
100
101     /* Offscreen image we draw to */
102     Pixmap off_map;
103     unsigned long int *off_alpha;
104 };
105
106 static void *xrealloc(void *p, size_t size) {
107     void *ret;
108     if ((ret = realloc(p, size)) == NULL) {
109         fprintf(stderr, "%s: out of memory\n", progname);
110         exit(1);
111     }
112     return ret;
113 }
114
115 struct field *init_field(void) {
116     struct field *f = xrealloc(NULL, sizeof(struct field));
117     f->height = 0;
118     f->width = 0;
119     f->initial_discs = 0;
120     f->discs = NULL;
121     f->num = 0;
122     f->maxrider = 0;
123     f->maxradius = 0;
124     f->cycles = 0;
125     f->fgcolor = 0;
126     f->bgcolor = 0;
127     f->off_alpha = NULL;
128     f->visdepth = 0;
129     return f;
130 }
131
132 /* Quick-ref to pixels in the alpha map */
133 #define ref_pixel(f, x, y)   ((f)->off_alpha[(y) * (f)->width + (x)])
134
135 inline void make_disc(struct field *f, float x, float y, float vx, float vy, float r) {
136     /* Synthesis of Disc::Disc and PxRider::PxRider */
137     Disc *nd;
138     int ix;
139
140     /* allocate a new disc */
141     f->discs = (Disc *) xrealloc(f->discs, sizeof(Disc) * (f->num + 1));
142
143     nd = &(f->discs[f->num]);
144
145     nd->id = f->num++;
146     nd->x = x;
147     nd->y = y;
148     nd->vx = vx;
149     nd->vy = vy;
150     nd->dr = r;
151     nd->r = frand(r) / 3;
152
153     nd->numr = (frand(r) / 2.62);
154     if (nd->numr > f->maxrider)
155         nd->numr = f->maxrider;
156
157     nd->pxRiders = NULL;
158     nd->pxRiders = (PxRider *) xrealloc(nd->pxRiders, sizeof(PxRider) * (f->maxrider));
159     for (ix = 0; ix < f->maxrider; ix++) {
160         nd->pxRiders[ix].vt = 0.0;
161         nd->pxRiders[ix].t = frand(M_PI * 2);
162         nd->pxRiders[ix].mycharge = 0;
163     }
164 }
165
166 inline void point2rgb(int depth, unsigned long c, unsigned short int *r, 
167                       unsigned short int *g, unsigned short int *b) {
168     switch(depth) {
169         case 32:
170         case 24:
171             *g = (c & 0xff00) >> 8; 
172             *r = (c & 0xff0000) >> 16; 
173             *b = c & 0xff; 
174             break;
175         case 16:
176             *g = ((c >> 5) & 0x3f) << 2;
177             *r = ((c >> 11) & 0x1f) << 3; 
178             *b = (c & 0x1f) << 3; 
179             break;
180         case 15:
181             *g = ((c >> 5) & 0x1f) << 3;
182             *r = ((c >> 10) & 0x1f) << 3;
183             *b = (c & 0x1f) << 3;
184             break;
185     }
186 }
187
188 inline unsigned long rgb2point(int depth, unsigned short int r, 
189                                unsigned short int g, unsigned short int b) {
190     unsigned long ret = 0;
191
192     switch(depth) {
193         case 32:
194             ret = 0xff000000;
195         case 24:
196             ret |= (r << 16) | (g << 8) | b;
197             break;
198         case 16:
199             ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
200             break;
201         case 15:
202             ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
203             break;
204     }
205
206     return ret;
207 }
208
209 /* alpha blended point drawing */
210 inline unsigned long trans_point(int x1, int y1, unsigned long myc, float a, struct field *f) {
211     if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
212         if (a >= 1.0) {
213             ref_pixel(f, x1, y1) = myc;
214         } else {
215             unsigned short int or = 0, og = 0, ob = 0;
216             unsigned short int r = 0, g = 0, b = 0;
217             unsigned short int nr, ng, nb;
218             unsigned long c;
219
220             c = ref_pixel(f, x1, y1);
221             point2rgb(f->visdepth, c, &or, &og, &ob);
222             point2rgb(f->visdepth, myc, &r, &g, &b);
223
224             nr = or + (r - or) * a;
225             ng = og + (g - og) * a;
226             nb = ob + (b - ob) * a;
227
228             c = rgb2point(f->visdepth, nr, ng, nb);
229             ref_pixel(f, x1, y1) = c;
230
231             return c;
232         }
233     }
234
235     return 0;
236 }
237
238 inline void move_disc(struct field *f, int dnum) {
239     Disc *d = &(f->discs[dnum]);
240
241     /* add velocity to position */
242     d->x += d->vx;
243     d->y += d->vy;
244
245     /* bound check */
246     if (d->x + d->r < 0)
247         d->x += f->width + d->r + d->r;
248     if (d->x - d->r > f->width)
249         d->x -= f->width + d->r + d->r;
250     if (d->y + d->r < 0)
251         d->y += f->height + d->r + d->r;
252     if (d->y - d->r > f->height)
253         d->y -= f->height + d->r + d->r;
254
255     /* increase to destination radius */
256     if (d->r < d->dr)
257         d->r += 0.1;
258 }
259
260 inline void draw_glowpoint(Display *dpy, Window window, GC fgc, struct field *f, float px, float py) {
261     int i, j;
262     float a;
263     unsigned long c;
264
265     for (i =- 2; i < 3; i++) {
266         for (j =- 2; j < 3; j++) {
267             a = 0.8 - i * i * 0.1 - j * j * 0.1;
268
269             c = trans_point(px+i, py+j, f->fgcolor, a, f);
270             XSetForeground(dpy, fgc, c);
271             XDrawPoint(dpy, window, fgc, px + i, py + j);
272             XSetForeground(dpy, fgc, f->fgcolor);
273         }
274     }
275 }
276
277 inline void moverender_rider(Display *dpy, Window window, GC fgc, struct field *f, PxRider *rid, 
278                              float x, float y, float r) {
279     float px, py;
280     unsigned long int c;
281     unsigned short int cr, cg, cb;
282     int ch;
283     double cs, cv;
284
285     /* add velocity to theta */
286     rid->t = fmod((rid->t + rid->vt + M_PI), (2 * M_PI)) - M_PI;
287     
288     rid->vt += frand(0.002) - 0.001;
289
290     /* apply friction brakes */
291     if (abs(rid->vt) > 0.02)
292         rid->vt *= 0.9;
293
294     /* draw */
295     px = x + r * cos(rid->t);
296     py = y + r * sin(rid->t);
297
298     if ((px < 0) || (px >= f->width) || (py < 0) || (py >= f->height))
299         return;
300
301     /* max brightness seems to be 0.003845 */
302
303     c = ref_pixel(f, (int) px, (int) py);
304     point2rgb(f->visdepth, c, &cr, &cg, &cb);
305     rgb_to_hsv(cr, cg, cb, &ch, &cs, &cv);
306
307     /* guestimated - 40 is 18% of 255, so scale this to 0.0 to 0.003845 */
308     if (cv > 0.0006921) {
309         draw_glowpoint(dpy, window, fgc, f, px, py); 
310
311         rid->mycharge = 0.003845;
312     } else {
313         rid->mycharge *= 0.98;
314
315         hsv_to_rgb(ch, cs, rid->mycharge, &cr, &cg, &cb);
316         c = rgb2point(f->visdepth, cr, cg, cb);
317
318         trans_point(px, py, c, 0.5, f);
319
320         XSetForeground(dpy, fgc, c);
321         XDrawPoint(dpy, window, fgc, px, py);
322         XSetForeground(dpy, fgc, f->fgcolor);
323     }
324 }
325
326 inline void render_disc(Display *dpy, Window window, GC fgc, struct field *f, int dnum) {
327     Disc *di = &(f->discs[dnum]);
328     int n, m;
329     float dx, dy, d;
330     float a, p2x, p2y, h, p3ax, p3ay, p3bx, p3by;
331     unsigned long c;
332
333     /* Find intersecting points with all ascending discs */
334     for (n = di->id + 1; n < f->num; n++) {
335         dx = f->discs[n].x - di->x;
336         dy = f->discs[n].y - di->y;
337         d = sqrt(dx * dx + dy * dy);
338
339         /* intersection test */
340         if (d < (f->discs[n].r + di->r)) {
341             /* complete containment test */
342             if (d > abs(f->discs[n].r - di->r)) {
343                 /* find solutions */
344                 a = (di->r * di->r - f->discs[n].r * f->discs[n].r + d * d) / (2 * d);
345                 p2x = di->x + a * (f->discs[n].x - di->x) / d;
346                 p2y = di->y + a * (f->discs[n].y - di->y) / d;
347
348                 h = sqrt(di->r * di->r - a * a);
349
350                 p3ax = p2x + h * (f->discs[n].y - di->y) / d;
351                 p3ay = p2y - h * (f->discs[n].x - di->x) / d;
352
353                 p3bx = p2x - h * (f->discs[n].y - di->y) / d;
354                 p3by = p2y + h * (f->discs[n].x - di->x) / d;
355
356                 /* bounds check */
357                 if ((p3ax < 0) || (p3ax >= f->width) || (p3ay < 0) || (p3ay >= f->height) ||
358                     (p3bx < 0) || (p3bx >= f->width) || (p3by < 0) || (p3by >= f->height))
359                     continue;
360                 
361                 /* p3a and p3b might be identical, ignore this case for now */
362                 /* XPutPixel(f->off_map, p3ax, p3ay, f->fgcolor); */
363                 c = trans_point(p3ax, p3ay, f->fgcolor, 0.75, f);
364                 XSetForeground(dpy, fgc, c);
365                 XDrawPoint(dpy, window, fgc, p3ax, p3ay);
366
367                 /* XPutPixel(f->off_map, p3bx, p3by, f->fgcolor); */
368                 c = trans_point(p3bx, p3by, f->fgcolor, 0.75, f);
369                 XSetForeground(dpy, fgc, c);
370                 XDrawPoint(dpy, window, fgc, p3bx, p3by);
371                 XSetForeground(dpy, fgc, f->fgcolor);
372             }
373         }
374
375     }
376
377     /* Render all the pixel riders */
378     for (m = 0; m < di->numr; m++) {
379         moverender_rider(dpy, window, fgc, f, &(di->pxRiders[m]), 
380                          di->x, di->y, di->r);
381     }
382 }
383
384 char *progclass = "InterMomentary";
385
386 char *defaults[] = {
387     ".background: black",
388     ".foreground: white",
389     "*drawDelay: 30000",
390     "*numDiscs: 85",
391     "*maxRiders: 40",
392     "*maxRadius: 100",
393     0
394 };
395
396 XrmOptionDescRec options[] = {
397     {"-background", ".background", XrmoptionSepArg, 0},
398     {"-foreground", ".foreground", XrmoptionSepArg, 0},
399     {"-draw-delay", ".drawDelay", XrmoptionSepArg, 0},
400     {"-num-discs", ".numDiscs", XrmoptionSepArg, 0},
401     {"-max-riders", ".maxRiders", XrmoptionSepArg, 0},
402     {"-max-radius", ".maxRadius", XrmoptionSepArg, 0},
403     {0, 0, 0, 0}
404 };
405
406 void build_img(Display *dpy, Window window, struct field *f) {
407     if (f->off_alpha) {
408         free(f->off_alpha);
409         f->off_alpha = NULL;
410
411         /* Assume theres also an off pixmap */
412         XFreePixmap(dpy, f->off_map);
413     }
414
415     f->off_alpha = (unsigned long *) xrealloc(f->off_alpha, sizeof(unsigned long) * 
416                                               f->width * f->height);
417
418     memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
419
420     f->off_map = XCreatePixmap(dpy, window, f->width, f->height, f->visdepth);
421
422 }
423
424 inline void blank_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, struct field *f) {
425     memset(f->off_alpha, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
426
427     XSetForeground(dpy, fgc, f->bgcolor);
428     XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
429     XSetForeground(dpy, fgc, f->fgcolor);
430 }
431
432 void screenhack(Display * dpy, Window window)
433 {
434     struct field *f = init_field();
435
436 #ifdef TIME_ME
437     time_t start_time = time(NULL);
438 #endif
439
440     int draw_delay = 0;
441     int tempx;
442
443     GC fgc, copygc;
444     XGCValues gcv;
445     XWindowAttributes xgwa;
446
447     draw_delay = (get_integer_resource("drawDelay", "Integer"));
448     f->maxrider = (get_integer_resource("maxRiders", "Integer"));
449     f->maxradius = (get_integer_resource("maxRadius", "Integer"));
450     f->initial_discs = (get_integer_resource("numDiscs", "Integer"));
451
452     if (f->initial_discs <= 10) {
453         fprintf(stderr, "%s: Initial discs must be greater than 10\n", progname);
454         return;
455     }
456
457     if (f->maxradius <= 30) {
458         fprintf(stderr, "%s: Max radius must be greater than 30\n", progname);
459         return;
460     }
461
462     if (f->maxrider <= 10) {
463         fprintf(stderr, "%s: Max riders must be greater than 10\n", progname);
464         return;
465     }
466     
467     XGetWindowAttributes(dpy, window, &xgwa);
468
469     f->height = xgwa.height;
470     f->width = xgwa.width;
471     f->visdepth = xgwa.depth;
472  
473     gcv.foreground = get_pixel_resource("foreground", "Foreground",
474                                         dpy, xgwa.colormap);
475     gcv.background = get_pixel_resource("background", "Background",
476                                         dpy, xgwa.colormap);
477     fgc = XCreateGC(dpy, window, GCForeground, &gcv);
478     copygc = XCreateGC(dpy, window, GCForeground, &gcv);
479
480     f->fgcolor = gcv.foreground;
481     f->bgcolor = gcv.background;
482
483     /* Initialize stuff */
484     build_img(dpy, window, f);
485
486     for (tempx = 0; tempx < f->initial_discs; tempx++) {
487         float fx, fy, x, y, r;
488         int bt;
489
490         /* Arrange in anti-collapsing circle */
491         fx = 0.4 * f->width * cos((2 * M_PI) * tempx / f->initial_discs);
492         fy = 0.4 * f->height * sin((2 * M_PI) * tempx / f->initial_discs);
493         x = frand(f->width / 2) + fx;
494         y = frand(f->height / 2) + fy;
495         r = 5 + frand(f->maxradius);
496         bt = 1;
497
498         if ((random() % 100) < 50)
499             bt = -1;
500
501         make_disc(f, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r);
502         
503     }
504     
505     while (1) {
506         if ((f->cycles % 10) == 0) {
507             /* Restart if the window size changes */
508             XGetWindowAttributes(dpy, window, &xgwa);
509
510             if (f->height != xgwa.height || f->width != xgwa.width) {
511                 f->height = xgwa.height;
512                 f->width = xgwa.width;
513                 f->visdepth = xgwa.depth;
514
515                 build_img(dpy, window, f);
516             }
517         }
518
519         blank_img(dpy, f->off_map, xgwa, fgc, f);
520         for (tempx = 0; tempx < f->num; tempx++) {
521             move_disc(f, tempx);
522             render_disc(dpy, f->off_map, fgc, f, tempx);
523         }
524
525         XSetFillStyle(dpy, copygc, FillTiled);
526         XSetTile(dpy, copygc, f->off_map);
527         XFillRectangle(dpy, window, copygc, 0, 0, f->width, f->height);
528
529         f->cycles++;
530
531 /*        XSync(dpy, False); */
532
533         screenhack_handle_events(dpy);
534
535         if (draw_delay)
536             usleep(draw_delay);
537     }
538 }