f372c1eeff7c8463bda4cda0fe5c826c0c9fa7aa
[xscreensaver] / hacks / lmorph.c
1
2 /**************************************************************************
3  *
4  *  FILE            lmorph.c
5  *  MODULE OF       xscreensaver
6  *
7  *  DESCRIPTION     Bilinear interpolation for morphing line shapes.
8  *
9  *  WRITTEN BY      Sverre H. Huseby                Glenn T. Lines
10  *                  Kurvn. 30                       Ã˜stgaardsgt. 5
11  *                  N-0495 Oslo                     N-0474 Oslo
12  *                  Norway                          Norway
13  *
14  *                  Phone:  +47 901 63 579          Phone:  +47 22 04 67 28
15  *                  E-mail: sverrehu@online.no      E-mail: gtl@si.sintef.no
16  *                  URL:    http://home.sol.no/~sverrehu/
17  *
18  *                  The original idea, and the bilinear interpolation
19  *                  mathematics used, emerged in the head of the wise
20  *                  Glenn T. Lines.
21  *
22  *  MODIFICATIONS   june 1998 (shh)
23  *                    * Minor code cleanup.
24  *
25  *                  march 1997 (shh)
26  *                    * Added -mailfile option to allow checking for
27  *                      new mail while the screensaver is active.
28  *
29  *                  january 1997 (shh)
30  *                    * Some code reformatting.
31  *                    * Added possibility to use float arithmetic.
32  *                    * Added -figtype option.
33  *                    * Made color blue default.
34  *
35  *                  december 1995 (jwz)
36  *                    * Function headers converted from ANSI to K&R.
37  *                    * Added posibility for random number of steps, and
38  *                      made this the default.
39  *
40  *                  march 1995 (shh)
41  *                    * Converted from an MS-Windows program to X Window.
42  *
43  **************************************************************************/
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <math.h>
49 #include "screenhack.h"
50
51 /**************************************************************************
52  *                                                                        *
53  *                       P R I V A T E    D A T A                         *
54  *                                                                        *
55  **************************************************************************/
56
57 /* define MARGINS to make some space around the figure. */
58 #define MARGINS
59
60 /* define USE_FLOAT to avoid using integer calculations in
61    createPoints. integer calculation is supposed to be faster, but it
62    won't work for displays larger than 2048x2048 or so pixels. */
63 #undef USE_FLOAT
64
65 #define MAXFIGS    20
66 #define TWO_PI     (2.0 * M_PI)
67 #define RND(x)     (random() % (x))
68
69 #define FT_OPEN    1
70 #define FT_CLOSED  2
71 #define FT_ALL     (FT_OPEN | FT_CLOSED)
72
73 static int
74     numFigs = 0,                /* number of figure arrays. */
75     numPoints,                  /* number of points in each array. */
76     nWork,                      /* current work array number. */
77     nFrom,                      /* current from array number. */
78     nTo,                        /* current to array number. */
79     figType;
80 static long delay;              /* usecs to wait between updates. */
81 static XPoint
82     *aWork[2],                  /* working arrays. */
83     *a[MAXFIGS],                /* the figure arrays. */
84     *aTmp,                      /* used as source when interrupting morph */
85     *aPrev,                     /* previous points displayed. */
86     *aCurr,                     /* the current points displayed. */  
87     *aFrom,                     /* figure converting from. */
88     *aTo;                       /* figure converting to. */
89 static int         scrWidth, scrHeight;
90 static double      currGamma, maxGamma = 1.0, deltaGamma;
91 static GC          gcDraw, gcClear;
92 static Display     *dpy;
93 static Window      window;
94
95
96
97 /**************************************************************************
98  *                                                                        *
99  *                        P U B L I C    D A T A                          *
100  *                                                                        *
101  **************************************************************************/
102
103 char *progclass = "LMorph";
104
105 char *defaults [] = {
106     ".background: black",
107     ".foreground: blue",
108     "*points: 150",
109     "*steps: 0",
110     "*delay: 50000",
111     "*figtype: all",
112     0
113 };
114
115 XrmOptionDescRec options [] = {
116   { "-points",      ".points",      XrmoptionSepArg, 0 },
117   { "-steps",       ".steps",       XrmoptionSepArg, 0 },
118   { "-delay",       ".delay",       XrmoptionSepArg, 0 },
119   { "-figtype",     ".figtype",     XrmoptionSepArg, 0 },
120   { 0, 0, 0, 0 }
121 };
122 int options_size = (sizeof (options) / sizeof (options[0]));
123
124
125
126 /**************************************************************************
127  *                                                                        *
128  *                   P R I V A T E    F U N C T I O N S                   *
129  *                                                                        *
130  **************************************************************************/
131
132 static void *
133 xmalloc(size_t size)
134 {
135     void *ret;
136
137     if ((ret = malloc(size)) == NULL) {
138         fprintf(stderr, "lmorph: out of memory\n");
139         exit(1);
140     }
141     return ret;
142 }
143
144 static void
145 initPointArrays(void)
146 {
147     int q, w,
148         mx, my,                 /* max screen coordinates. */
149         mp,                     /* max point number. */
150         s, rx, ry,
151         marginx, marginy;
152     double scalex, scaley;
153
154     mx = scrWidth - 1;
155     my = scrHeight - 1;
156     mp = numPoints - 1;
157
158     aWork[0] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
159     aWork[1] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
160     aTmp     = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
161
162     if (figType & FT_CLOSED) {
163         /* rectangle */
164         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
165         s = numPoints / 4;
166         for (q = 0; q < s; q++) {
167             a[numFigs][q].x = ((double) q / s) * mx;
168             a[numFigs][q].y = 0;
169             a[numFigs][s + q].x = mx;
170             a[numFigs][s + q].y = ((double) q / s) * my;
171             a[numFigs][2 * s + q].x = mx - ((double) q / s) * mx;
172             a[numFigs][2 * s + q].y = my;
173             a[numFigs][3 * s + q].x = 0;
174             a[numFigs][3 * s + q].y = my - ((double) q / s) * my;
175         }
176         for (q = 4 * s; q < numPoints; q++) 
177             a[numFigs][q].x = a[numFigs][q].y = 0;
178         a[numFigs][mp].x = a[numFigs][0].x;
179         a[numFigs][mp].y = a[numFigs][0].y;
180         ++numFigs;
181
182         /*  */
183         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
184         rx = mx / 2;
185         ry = my / 2;
186         for (q = 0; q < numPoints; q++) {
187             a[numFigs][q].x = mx / 2 + rx * sin(1 * TWO_PI * (double) q / mp);
188             a[numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
189         }
190         a[numFigs][mp].x = a[numFigs][0].x;
191         a[numFigs][mp].y = a[numFigs][0].y;
192         ++numFigs;
193
194         /*  */
195         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
196         rx = mx / 2;
197         ry = my / 2;
198         for (q = 0; q < numPoints; q++) {
199             a[numFigs][q].x = mx / 2 + ry * sin(3 * TWO_PI * (double) q / mp);
200             a[numFigs][q].y = my / 2 + ry * cos(1 * TWO_PI * (double) q / mp);
201         }
202         a[numFigs][mp].x = a[numFigs][0].x;
203         a[numFigs][mp].y = a[numFigs][0].y;
204         ++numFigs;
205
206         /*  */
207         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
208         rx = mx / 2;
209         ry = my / 2;
210         for (q = 0; q < numPoints; q++) {
211             a[numFigs][q].x = mx / 2 + ry 
212                 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
213                 * sin(TWO_PI * (double) q / mp);
214             a[numFigs][q].y = my / 2 + ry
215                 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
216                 * cos(TWO_PI * (double) q / mp);
217         }
218         a[numFigs][mp].x = a[numFigs][0].x;
219         a[numFigs][mp].y = a[numFigs][0].y;
220         ++numFigs;
221
222         /*  */
223         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
224         rx = mx / 2;
225         ry = my / 2;
226         for (q = 0; q < numPoints; q++) {
227             a[numFigs][q].x = mx / 2 + ry * sin(TWO_PI * (double) q / mp);
228             a[numFigs][q].y = my / 2 + ry * cos(TWO_PI * (double) q / mp);
229         }
230         a[numFigs][mp].x = a[numFigs][0].x;
231         a[numFigs][mp].y = a[numFigs][0].y;
232         ++numFigs;
233
234         /*  */
235         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
236         rx = mx / 2;
237         ry = my / 2;
238         for (q = 0; q < numPoints; q++) {
239             a[numFigs][q].x = mx / 2 + rx * cos(TWO_PI * (double) q / mp);
240             a[numFigs][q].y = my / 2 + ry * sin(TWO_PI * (double) q / mp);
241         }
242         a[numFigs][mp].x = a[numFigs][0].x;
243         a[numFigs][mp].y = a[numFigs][0].y;
244         ++numFigs;
245
246         /*  */
247         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
248         rx = mx / 2;
249         ry = my / 2;
250         for (q = 0; q < numPoints; q++) {
251             a[numFigs][q].x = mx / 2 + rx * sin(2 * TWO_PI * (double) q / mp);
252             a[numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
253         }
254         a[numFigs][mp].x = a[numFigs][0].x;
255         a[numFigs][mp].y = a[numFigs][0].y;
256         ++numFigs;
257     }
258
259     if (figType & FT_OPEN) {
260         /* sine wave, one period */
261         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
262         for (q = 0; q < numPoints; q++) {
263             a[numFigs][q].x = ((double) q / numPoints) * mx;
264             a[numFigs][q].y = (1.0 - sin(((double) q / mp) * TWO_PI))
265                 * my / 2.0;
266         }
267         ++numFigs;
268
269         /*  */
270         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
271         for (q = 0; q < numPoints; q++) {
272             a[numFigs][q].x = ((double) q / mp) * mx;
273             a[numFigs][q].y = (1.0 - cos(((double) q / mp) * 3 * TWO_PI))
274                 * my / 2.0;
275         }
276         ++numFigs;
277
278         /* spiral, one endpoint at bottom */
279         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
280         rx = mx / 2;
281         ry = my / 2;
282         for (q = 0; q < numPoints; q++) {
283             a[numFigs][q].x = mx / 2 + ry * sin(5 * TWO_PI * (double) q / mp)
284                 * ((double) q / mp);
285             a[numFigs][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp)
286                 * ((double) q / mp);
287         }
288         ++numFigs;
289
290         /* spiral, one endpoint at top */
291         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
292         rx = mx / 2;
293         ry = my / 2;
294         for (q = 0; q < numPoints; q++) {
295             a[numFigs][q].x = mx / 2 + ry * sin(6 * TWO_PI * (double) q / mp)
296                 * ((double) q / mp);
297             a[numFigs][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp)
298                 * ((double) q / mp);
299         }
300         ++numFigs;
301
302         /*  */
303         a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
304         for (q = 0; q < numPoints; q++) {
305             a[numFigs][q].x = ((double) q / mp) * mx;
306             a[numFigs][q].y = (1.0 - sin(((double) q / mp) * 5 * TWO_PI))
307                 * my / 2.0;
308         }
309         ++numFigs;
310     }
311
312 #ifdef MARGINS
313     /* make some space around the figures.  */
314     marginx = (mx + 1) / 10;
315     marginy = (my + 1) / 10;
316     scalex = (double) ((mx + 1) - 2.0 * marginx) / (mx + 1.0);
317     scaley = (double) ((my + 1) - 2.0 * marginy) / (my + 1.0);
318     for (q = 0; q < numFigs; q++)
319         for (w = 0; w < numPoints; w++) {
320             a[q][w].x = marginx + a[q][w].x * scalex;
321             a[q][w].y = marginy + a[q][w].y * scaley;
322         }
323 #endif
324 }
325
326 static void
327 initLMorph(void)
328 {
329     int               steps;
330     XGCValues         gcv;
331     XWindowAttributes wa;
332     Colormap          cmap;
333     char              *ft;
334     
335     numPoints = get_integer_resource("points", "Integer");
336     steps = get_integer_resource("steps", "Integer");
337     delay = get_integer_resource("delay", "Integer");
338     ft = get_string_resource("figtype", "String");
339
340     if (strcmp(ft, "all") == 0)
341         figType = FT_ALL;
342     else if (strcmp(ft, "open") == 0)
343         figType = FT_OPEN;
344     else if (strcmp(ft, "closed") == 0)
345         figType = FT_CLOSED;
346     else {
347         fprintf(stderr, "figtype should be `all', `open' or `closed'.\n");
348         figType = FT_ALL;
349     }
350
351     if (steps <= 0)
352       steps = (random() % 400) + 100;
353
354     deltaGamma = 1.0 / steps;
355     XGetWindowAttributes(dpy, window, &wa);
356     scrWidth = wa.width;
357     scrHeight = wa.height;
358     cmap = wa.colormap;
359     gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
360     gcDraw = XCreateGC(dpy, window, GCForeground, &gcv);
361     XSetForeground(dpy, gcDraw, gcv.foreground);
362     gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
363     gcClear = XCreateGC(dpy, window, GCForeground, &gcv);
364     XClearWindow(dpy, window);
365
366     srandom(time(NULL));
367     initPointArrays();
368     aCurr = aWork[nWork = 0];
369     aPrev = NULL;
370     currGamma = maxGamma + 1.0;  /* force creation of new figure at startup */
371     nTo = RND(numFigs);
372
373     { /* jwz for version 2.11 */
374       int width = random() % 10;
375       int style = LineSolid;
376       int cap   = (width > 1 ? CapRound  : CapButt);
377       int join  = (width > 1 ? JoinRound : JoinBevel);
378       if (width == 1) width = 0;
379       XSetLineAttributes(dpy, gcDraw,  width, style, cap, join);
380       XSetLineAttributes(dpy, gcClear, width, style, cap, join);
381     }
382 }
383
384 /* 55% of execution time */
385 static void
386 createPoints(void)
387 {
388     int             q;
389     XPoint *pa = aCurr, *pa1 = aFrom, *pa2 = aTo;
390 #ifdef USE_FLOAT
391     float           fg, f1g;
392 #else
393     long            lg, l1g;
394 #endif
395
396 #ifdef USE_FLOAT
397     fg  = currGamma;
398     f1g = 1.0 - currGamma;
399 #else
400     lg  = 8192L * currGamma;
401     l1g = 8192L * (1.0 - currGamma);
402 #endif
403     for (q = numPoints; q; q--) {
404 #ifdef USE_FLOAT
405         pa->x = (short) (f1g * pa1->x + fg * pa2->x);
406         pa->y = (short) (f1g * pa1->y + fg * pa2->y);
407 #else
408         pa->x = (short) ((l1g * pa1->x + lg * pa2->x) / 8192L);
409         pa->y = (short) ((l1g * pa1->y + lg * pa2->y) / 8192L);
410 #endif
411         ++pa;
412         ++pa1;
413         ++pa2;
414     }
415 }
416
417 /* 36% of execution time */
418 static void
419 drawImage(void)
420 {
421     int             q;
422     XPoint *old0, *old1, *new0, *new1;
423
424     /* Problem: update the window without too much flickering. I do
425      * this by handling each linesegment separately. First remove a
426      * line, then draw the new line. The problem is that this leaves
427      * small black pixels on the figure. To fix this, I draw the
428      * entire figure using XDrawLines() afterwards. */
429     if (aPrev) {
430         old0 = aPrev;
431         old1 = aPrev + 1;
432         new0 = aCurr;
433         new1 = aCurr + 1;
434         for (q = numPoints - 1; q; q--) {
435             XDrawLine(dpy, window, gcClear,
436                       old0->x, old0->y, old1->x, old1->y);
437             XDrawLine(dpy, window, gcDraw,
438                       new0->x, new0->y, new1->x, new1->y);
439             ++old0;
440             ++old1;
441             ++new0;
442             ++new1;
443         }
444     }
445     XDrawLines(dpy, window, gcDraw, aCurr, numPoints, CoordModeOrigin);
446
447     XFlush(dpy);
448 }
449
450 /* neglectible % of execution time */
451 static void
452 animateLMorph(void)
453 {
454     if (currGamma > maxGamma) {
455         currGamma = 0.0;
456         if (maxGamma == 1.0) {
457             nFrom = nTo;
458             aFrom = a[nFrom];
459         } else {
460             memcpy(aTmp, aCurr, numPoints * sizeof(XPoint));
461             aFrom = aTmp;
462             nFrom = -1;
463         }
464         do {
465             nTo = RND(numFigs);
466         } while (nTo == nFrom);
467         aTo = a[nTo];
468         if (RND(2)) {
469             /* reverse the array to get more variation. */
470             int    i1, i2;
471             XPoint p;
472             
473             for (i1 = 0, i2 = numPoints - 1; i1 < numPoints / 2; i1++, i2--) {
474                 p = aTo[i1];
475                 aTo[i1] = aTo[i2];
476                 aTo[i2] = p;
477             }
478         }
479         /* occationally interrupt the next run. */
480         if (RND(4) == 0)
481             maxGamma = 0.1 + 0.7 * (RND(1001) / 1000.0); /* partial run */
482         else
483             maxGamma = 1.0;                              /* full run */
484     }
485
486     createPoints();
487     drawImage();
488     aPrev = aCurr;
489     aCurr = aWork[nWork ^= 1];
490
491     currGamma += deltaGamma;
492 }
493
494
495
496 /**************************************************************************
497  *                                                                        *
498  *                    P U B L I C    F U N C T I O N S                    *
499  *                                                                        *
500  **************************************************************************/
501
502 void
503 screenhack(Display *disp, Window win)
504 {
505     dpy = disp;
506     window = win;
507     initLMorph();
508     for (;;) {
509         animateLMorph();
510         screenhack_usleep(delay);
511     }
512 }