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