70c91eee893e5c730166100eaeb45be3c0f4fc6b
[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   { 0, 0, 0, 0 }
95 };
96
97
98 /**************************************************************************
99  *                                                                        *
100  *                   P R I V A T E    F U N C T I O N S                   *
101  *                                                                        *
102  **************************************************************************/
103
104 static void *
105 xmalloc(size_t size)
106 {
107     void *ret;
108
109     if ((ret = malloc(size)) == NULL) {
110         fprintf(stderr, "lmorph: out of memory\n");
111         exit(1);
112     }
113     return ret;
114 }
115
116
117
118 static double frnd (void)
119 {
120     /*
121      *  Hm. for some reason the second line (using RAND_MAX) didn't
122      *  work on some machines, so I always use the first.
123      */
124 #ifndef dont_use_RAND_MAX
125     return (double) (random() & 0x7FFF) / 0x7FFF;
126 #else  /* RAND_MAX */
127     return ((double) random()) / RAND_MAX;
128 #endif /* RAND_MAX */
129 }
130
131
132
133 static void initPointArrays (void)
134 {
135     XWindowAttributes wa;
136     int q, w,
137         mx, my,                 /* Max screen coordinates. */
138         mp,                     /* Max point number. */
139         s, rx, ry,
140         marginx, marginy;
141     double scalex, scaley;
142
143     XGetWindowAttributes(dpy, window, &wa);
144     mx = wa.width - 1;
145     my = wa.height - 1;
146     mp = cPoint - 1;
147
148     aWork[0] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
149     aWork[1] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
150     aTmp     = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
151
152
153     /*
154      *  Figure 0
155      */
156     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
157     s = cPoint / 4;
158     for (q = 0; q < s; q++) {
159         a[cFig][q].x = ((double) q / s) * mx;
160         a[cFig][q].y = 0;
161         a[cFig][s + q].x = mx;
162         a[cFig][s + q].y = ((double) q / s) * my;
163         a[cFig][2 * s + q].x = mx - ((double) q / s) * mx;
164         a[cFig][2 * s + q].y = my;
165         a[cFig][3 * s + q].x = 0;
166         a[cFig][3 * s + q].y = my - ((double) q / s) * my;
167     }
168     for (q = 4 * s; q < cPoint; q++) 
169         a[cFig][q].x = a[cFig][q].y = 0;
170     a[cFig][mp].x = a[cFig][0].x;
171     a[cFig][mp].y = a[cFig][0].y;
172     ++cFig;
173
174     /*
175      *  Figure 1
176      */
177     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
178     for (q = 0; q < cPoint; q++) {
179         a[cFig][q].x = ((double) q / cPoint) * mx;
180         a[cFig][q].y = (1.0 - sin(((double) q / mp) * TWO_PI)) * my / 2.0;
181     }
182     ++cFig;
183
184     /*
185      *  Figure 2
186      */
187     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
188     rx = mx / 2;
189     ry = my / 2;
190     for (q = 0; q < cPoint; q++) {
191         a[cFig][q].x = mx / 2 + rx * sin(1 * TWO_PI * (double) q / mp);
192         a[cFig][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
193     }
194     a[cFig][mp].x = a[cFig][0].x;
195     a[cFig][mp].y = a[cFig][0].y;
196     ++cFig;
197
198     /*
199      *  Figure 3
200      */
201     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
202     rx = mx / 2;
203     ry = my / 2;
204     for (q = 0; q < cPoint; q++) {
205         a[cFig][q].x = mx / 2 + ry * sin(3 * TWO_PI * (double) q / mp);
206         a[cFig][q].y = my / 2 + ry * cos(1 * TWO_PI * (double) q / mp);
207     }
208     a[cFig][mp].x = a[cFig][0].x;
209     a[cFig][mp].y = a[cFig][0].y;
210     ++cFig;
211
212     /*
213      *  Figure 4
214      */
215     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
216     rx = mx / 2;
217     ry = my / 2;
218     for (q = 0; q < cPoint; q++) {
219         a[cFig][q].x = mx / 2 + ry * (1 - 0.1 * frnd())
220             * sin(TWO_PI * (double) q / mp);
221         a[cFig][q].y = my / 2 + ry * (1 - 0.1 * frnd())
222             * cos(TWO_PI * (double) q / mp);
223     }
224     a[cFig][mp].x = a[cFig][0].x;
225     a[cFig][mp].y = a[cFig][0].y;
226     ++cFig;
227
228     /*
229      *  Figure 5
230      */
231     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
232     rx = mx / 2;
233     ry = my / 2;
234     for (q = 0; q < cPoint; q++) {
235         a[cFig][q].x = mx / 2 + ry * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
236             * sin(TWO_PI * (double) q / mp);
237         a[cFig][q].y = my / 2 + ry * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
238             * cos(TWO_PI * (double) q / mp);
239     }
240     a[cFig][mp].x = a[cFig][0].x;
241     a[cFig][mp].y = a[cFig][0].y;
242     ++cFig;
243
244     /*
245      *  Figure 6
246      */
247     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
248     rx = mx / 2;
249     ry = my / 2;
250     for (q = 0; q < cPoint; q++) {
251         a[cFig][q].x = mx / 2 + ry * sin(TWO_PI * (double) q / mp);
252         a[cFig][q].y = my / 2 + ry * cos(TWO_PI * (double) q / mp);
253     }
254     a[cFig][mp].x = a[cFig][0].x;
255     a[cFig][mp].y = a[cFig][0].y;
256     ++cFig;
257
258     /*
259      *  Figure 7
260      */
261     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
262     rx = mx / 2;
263     ry = my / 2;
264     for (q = 0; q < cPoint; q++) {
265         a[cFig][q].x = mx / 2 + rx * cos(TWO_PI * (double) q / mp);
266         a[cFig][q].y = my / 2 + ry * sin(TWO_PI * (double) q / mp);
267     }
268     a[cFig][mp].x = a[cFig][0].x;
269     a[cFig][mp].y = a[cFig][0].y;
270     ++cFig;
271
272     /*
273      *  Figure 8
274      */
275     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
276     for (q = 0; q < cPoint; q++) {
277         a[cFig][q].x = ((double) q / mp) * mx;
278         a[cFig][q].y = (1.0 - cos(((double) q / mp) * 3 * TWO_PI)) * my / 2.0;
279     }
280     ++cFig;
281
282     /*
283      *  Figure 9
284      */
285     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
286     rx = mx / 2;
287     ry = my / 2;
288     for (q = 0; q < cPoint; q++) {
289         a[cFig][q].x = mx / 2 + rx * sin(2 * TWO_PI * (double) q / mp);
290         a[cFig][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
291     }
292     a[cFig][mp].x = a[cFig][0].x;
293     a[cFig][mp].y = a[cFig][0].y;
294     ++cFig;
295
296     /*
297      *  Figure 10
298      */
299     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
300     rx = mx / 2;
301     ry = my / 2;
302     for (q = 0; q < cPoint; q++) {
303         a[cFig][q].x = mx / 2 + ry * sin(5 * TWO_PI * (double) q / mp)
304             * ((double) q / mp);
305         a[cFig][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp)
306             * ((double) q / mp);
307     }
308     ++cFig;
309
310     /*
311      *  Figure 11
312      */
313     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
314     rx = mx / 2;
315     ry = my / 2;
316     for (q = 0; q < cPoint; q++) {
317         a[cFig][q].x = mx / 2 + ry * sin(6 * TWO_PI * (double) q / mp)
318             * ((double) q / mp);
319         a[cFig][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp)
320             * ((double) q / mp);
321     }
322     ++cFig;
323
324     /*
325      *  Figure 12
326      */
327     a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint));
328     for (q = 0; q < cPoint; q++) {
329         a[cFig][q].x = ((double) q / mp) * mx;
330         a[cFig][q].y = (1.0 - sin(((double) q / mp) * 5 * TWO_PI)) * my / 2.0;
331     }
332     ++cFig;
333
334 #ifdef MARGINS
335     /*
336      *  Make some space around the figures.
337      */
338     marginx = (mx + 1) / 10;
339     marginy = (my + 1) / 10;
340     scalex = (double) ((mx + 1) - 2.0 * marginx) / (mx + 1.0);
341     scaley = (double) ((my + 1) - 2.0 * marginy) / (my + 1.0);
342     for (q = 0; q < cFig; q++)
343         for (w = 0; w < cPoint; w++) {
344             a[q][w].x = marginx + a[q][w].x * scalex;
345             a[q][w].y = marginy + a[q][w].y * scaley;
346         }
347 #endif
348 }
349
350
351
352 static void createPoints (void)
353 {
354     int    q;
355     XPoint *pa = aCurr, *pa1 = aFrom, *pa2 = aTo;
356     long   lg, l1g;
357
358
359     lg = 8192L * gam, l1g = 8192L * (1.0 - gam);
360     for (q = 0; q < cPoint; q++) {
361         pa->x = (short) ((l1g * pa1->x + lg * pa2->x) / 8192L);
362         pa->y = (short) ((l1g * pa1->y + lg * pa2->y) / 8192L);
363         ++pa;
364         ++pa1;
365         ++pa2;
366     }
367 }
368
369
370 static void drawImage (void)
371 {
372     register int q;
373     XPoint *old0, *old1, *new0, *new1;
374
375     /*
376      *  Problem: update the window without too much flickering. I do
377      *  this by handling each linesegment separately. First remove a
378      *  line, then draw the new line. The problem is that this leaves
379      *  small black pixels on the figure. To fix this, I draw the
380      *  entire figure using XDrawLines() afterwards.
381      */
382     if (aPrev) {
383         old0 = aPrev;
384         old1 = aPrev + 1;
385         new0 = aCurr;
386         new1 = aCurr + 1;
387         for (q = cPoint - 1; q; q--) {
388             XDrawLine(dpy, window, gcClear,
389                       old0->x, old0->y, old1->x, old1->y);
390             XDrawLine(dpy, window, gcDraw,
391                       new0->x, new0->y, new1->x, new1->y);
392             ++old0;
393             ++old1;
394             ++new0;
395             ++new1;
396         }
397     }
398     XDrawLines(dpy, window, gcDraw, aCurr, cPoint, CoordModeOrigin);
399     XFlush(dpy);
400 }
401
402 static void initLMorph (void)
403 {
404     int               steps;
405     XGCValues         gcv;
406     XWindowAttributes wa;
407     Colormap          cmap;
408     
409     cPoint = get_integer_resource("points", "Integer");
410     steps = get_integer_resource("steps", "Integer");
411     delay = get_integer_resource("delay", "Integer");
412
413     if (steps <= 0)
414       steps = (random() % 400) + 100;
415
416     delta_gam = 1.0 / steps;
417     XGetWindowAttributes(dpy, window, &wa);
418     cmap = wa.colormap;
419     gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
420     gcDraw = XCreateGC(dpy, window, GCForeground, &gcv);
421     XSetForeground(dpy, gcDraw, gcv.foreground);
422     gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
423     gcClear = XCreateGC(dpy, window, GCForeground, &gcv);
424     XClearWindow(dpy, window);
425
426     initPointArrays();
427     aCurr = aWork[nWork = 0];
428     aPrev = NULL;
429     gam = 2.0;
430     nTo = RND(cFig);
431
432     {
433       int width = random() % 10;
434       int style = LineSolid;
435       int cap   = (width > 1 ? CapRound  : CapButt);
436       int join  = (width > 1 ? JoinRound : JoinBevel);
437       if (width == 1) width = 0;
438       XSetLineAttributes(dpy, gcDraw,  width, style, cap, join);
439       XSetLineAttributes(dpy, gcClear, width, style, cap, join);
440     }
441
442 }
443
444 static void animateLMorph (void)
445 {
446     if (gam > maxGamma) {
447         gam = 0.0;
448         if (maxGamma == 1.0) {
449             nFrom = nTo;
450             aFrom = a[nFrom];
451         } else {
452             memcpy(aTmp, aCurr, cPoint * sizeof(XPoint));
453             aFrom = aTmp;
454             nFrom = -1;
455         }
456         do {
457             nTo = RND(cFig);
458         } while (nTo == nFrom);
459         aTo = a[nTo];
460         if (RND(2)) {
461             /*
462              *  Reverse the array to get more variation.
463              */
464             int    i1, i2;
465             XPoint p;
466             
467             for (i1 = 0, i2 = cPoint - 1; i1 < cPoint / 2; i1++, i2--) {
468                 p = aTo[i1];
469                 aTo[i1] = aTo[i2];
470                 aTo[i2] = p;
471             }
472         }
473         /*
474          *  It may be nice to interrupt the next run.
475          */
476         if (RND(3) > 0)
477             maxGamma = 0.1 + 0.7 * (RND(1001) / 1000.0);
478         else
479             maxGamma = 1.0;
480     }
481
482     createPoints();
483     drawImage();
484     aPrev = aCurr;
485     aCurr = aWork[nWork ^= 1];
486
487     gam += delta_gam;
488 }
489
490
491
492 /**************************************************************************
493  *                                                                        *
494  *                    P U B L I C    F U N C T I O N S                    *
495  *                                                                        *
496  **************************************************************************/
497
498 void
499 screenhack(Display *disp, Window win)
500 {
501     dpy = disp;
502     window = win;
503     initLMorph();
504     for (;;) {
505         animateLMorph();
506         screenhack_usleep(delay);
507     }
508 }