http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / lmorph.c
1 /* lmorph, Copyright (c) 1993-1999 Sverre H. Huseby and Glenn T. Lines
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  */
11
12 /*------------------------------------------------------------------------
13  |
14  |  FILE            lmorph.c
15  |  MODULE OF       xscreensaver
16  |
17  |  DESCRIPTION     Smooth and non-linear morphing between 1D curves.
18  |
19  |  WRITTEN BY      Sverre H. Huseby                Glenn T. Lines
20  |                  Kurvn. 30                       Ã˜stgaardsgt. 5
21  |                  N-0495 Oslo                     N-0474 Oslo
22  |                  Norway                          Norway
23  |
24  |                  Phone:  +47 901 63 579          Phone:  +47 22 04 67 28
25  |                  E-mail: sverrehu@online.no      E-mail: glennli@ifi.uio.no
26  |                  URL:    http://home.sol.no/~sverrehu/
27  |
28  |                  The original idea, and the bilinear interpolation
29  |                  mathematics used, emerged in the head of the wise
30  |                  Glenn T. Lines.
31  |
32  |  MODIFICATIONS   october 1999 (shh)
33  |                    * Removed option to use integer arithmetic.
34  |                    * Increased default number of points, and brightened
35  |                      the foreground color a little bit.
36  |                    * Minor code cleanup (very minor, that is).
37  |                    * Default number of steps is no longer random.
38  |                    * Added -linewidth option (and resource).
39  |
40  |                  october 1999 (gtl)
41  |                    * Added cubic interpolation between shapes 
42  |                    * Added non-linear transformation speed  
43  |
44  |                  june 1998 (shh)
45  |                    * Minor code cleanup.
46  |
47  |                  january 1997 (shh)
48  |                    * Some code reformatting.
49  |                    * Added possibility to use float arithmetic.
50  |                    * Added -figtype option.
51  |                    * Made color blue default.
52  |
53  |                  december 1995 (jwz)
54  |                    * Function headers converted from ANSI to K&R.
55  |                    * Added posibility for random number of steps, and
56  |                      made this the default.
57  |
58  |                  march 1995 (shh)
59  |                    * Converted from an MS-Windows program to X Window.
60  |
61  |                  november 1993 (gtl, shh, lots of beer)
62  |                    * Original Windows version (we didn't know better).
63  +----------------------------------------------------------------------*/
64
65 #include <math.h>
66 #include "screenhack.h"
67
68 /*-----------------------------------------------------------------------+
69 |  PRIVATE DATA                                                          |
70 +-----------------------------------------------------------------------*/
71
72 /* define MARGINS to make some space around the figure. */
73 #define MARGINS
74
75 #define MAXFIGS    20
76 #define TWO_PI     (2.0 * M_PI)
77 #define RND(x)     (random() % (x))
78
79 #define FT_OPEN    1
80 #define FT_CLOSED  2
81 #define FT_ALL     (FT_OPEN | FT_CLOSED)
82
83 struct state {
84   Display *dpy;
85   Window window;
86
87    int numFigs;             /* number of figure arrays. */
88    int numPoints;           /* number of points in each array. */
89    int nWork;               /* current work array number. */
90    int nFrom;               /* current from array number. */
91    int nTo;                 /* current to array number. */
92    int nNext;               /* current next array number (after to).*/
93    int shift;               /* shifts the starting point of a figure */
94    int figType;
95
96    long delay;              /* usecs to wait between updates. */
97
98    XPoint *aWork[2];        /* working arrays. */
99    XPoint *a[MAXFIGS];      /* the figure arrays. */
100    XPoint *aTmp;            /* used as source when interrupting morph */
101    XPoint *aPrev;           /* previous points displayed. */
102    XPoint *aCurr;           /* the current points displayed. */  
103    XPoint *aFrom;           /* figure converting from. */
104    XPoint *aTo;             /* figure converting to. */
105    XPoint *aNext;           /* figure converting to next time. */
106    XPoint *aSlopeFrom;      /* slope at start of morph */ 
107    XPoint *aSlopeTo;        /* slope at end of morph */ 
108
109    int         scrWidth, scrHeight;
110    double      currGamma, maxGamma, deltaGamma;
111    GC          gcDraw, gcClear;
112 };
113
114
115 /*-----------------------------------------------------------------------+
116 |  PUBLIC DATA                                                           |
117 +-----------------------------------------------------------------------*/
118
119 static const char *lmorph_defaults [] = {
120     ".background: black",
121     ".foreground: #4444FF",
122     "*points: 200",
123     "*steps: 150",
124     "*delay: 70000",
125     "*figtype: all",
126     "*linewidth: 5",
127     0
128 };
129
130 static XrmOptionDescRec lmorph_options [] = {
131     { "-points",      ".points",      XrmoptionSepArg, 0 },
132     { "-steps",       ".steps",       XrmoptionSepArg, 0 },
133     { "-delay",       ".delay",       XrmoptionSepArg, 0 },
134     { "-figtype",     ".figtype",     XrmoptionSepArg, 0 },
135     { "-linewidth",   ".linewidth",   XrmoptionSepArg, 0 },
136     { 0, 0, 0, 0 }
137 };
138
139 /*-----------------------------------------------------------------------+
140 |  PRIVATE FUNCTIONS                                                     |
141 +-----------------------------------------------------------------------*/
142
143 static void *
144 xmalloc(size_t size)
145 {
146     void *ret;
147
148     if ((ret = malloc(size)) == NULL) {
149         fprintf(stderr, "lmorph: out of memory\n");
150         exit(1);
151     }
152     return ret;
153 }
154
155 static void
156 initPointArrays(struct state *st)
157 {
158     int    q, w;
159     int    mx, my;            /* max screen coordinates. */
160     int    mp;                /* max point number. */
161     int    s, rx, ry;
162     int    marginx, marginy;
163     double scalex, scaley;
164
165     mx = st->scrWidth - 1;
166     my = st->scrHeight - 1;
167     mp = st->numPoints - 1;
168
169     st->aWork[0] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
170     st->aWork[1] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
171     st->aTmp     = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
172
173     if (st->figType & FT_CLOSED) {
174         /* rectangle */
175         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
176         s = st->numPoints / 4;
177         for (q = 0; q < s; q++) {
178             st->a[st->numFigs][q].x = ((double) q / s) * mx;
179             st->a[st->numFigs][q].y = 0;
180             st->a[st->numFigs][s + q].x = mx;
181             st->a[st->numFigs][s + q].y = ((double) q / s) * my;
182             st->a[st->numFigs][2 * s + q].x = mx - ((double) q / s) * mx;
183             st->a[st->numFigs][2 * s + q].y = my;
184             st->a[st->numFigs][3 * s + q].x = 0;
185             st->a[st->numFigs][3 * s + q].y = my - ((double) q / s) * my;
186         }
187         for (q = 4 * s; q < st->numPoints; q++) 
188             st->a[st->numFigs][q].x = st->a[st->numFigs][q].y = 0;
189         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
190         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
191         ++st->numFigs;
192
193         /*  */
194         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
195         rx = mx / 2;
196         ry = my / 2;
197         for (q = 0; q < st->numPoints; q++) {
198             st->a[st->numFigs][q].x = mx / 2 + rx * sin(1 * TWO_PI * (double) q / mp);
199             st->a[st->numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
200         }
201         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
202         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
203         ++st->numFigs;
204
205         /*  */
206         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
207         rx = mx / 2;
208         ry = my / 2;
209         for (q = 0; q < st->numPoints; q++) {
210             st->a[st->numFigs][q].x = mx / 2 + ry * sin(3 * TWO_PI * (double) q / mp);
211             st->a[st->numFigs][q].y = my / 2 + ry * cos(1 * TWO_PI * (double) q / mp);
212         }
213         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
214         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
215         ++st->numFigs;
216
217         /*  */
218         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
219         rx = mx / 2;
220         ry = my / 2;
221         for (q = 0; q < st->numPoints; q++) {
222             st->a[st->numFigs][q].x = mx / 2 + ry 
223                 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
224                 * sin(TWO_PI * (double) q / mp);
225             st->a[st->numFigs][q].y = my / 2 + ry
226                 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
227                 * cos(TWO_PI * (double) q / mp);
228         }
229         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
230         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
231         ++st->numFigs;
232
233
234         /* */
235         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
236         rx = mx / 2;
237         ry = my / 2;
238         for (q = 0; q < st->numPoints; q++) {
239             st->a[st->numFigs][q].x = mx / 2 + ry * sin(TWO_PI * (double) q / mp);
240             st->a[st->numFigs][q].y = my / 2 + ry * cos(TWO_PI * (double) q / mp);
241         }
242         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
243         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
244         ++st->numFigs;
245
246
247         /*  */
248         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
249         rx = mx / 2;
250         ry = my / 2;
251         for (q = 0; q < st->numPoints; q++) {
252             st->a[st->numFigs][q].x = mx / 2 + rx * cos(TWO_PI * (double) q / mp);
253             st->a[st->numFigs][q].y = my / 2 + ry * sin(TWO_PI * (double) q / mp);
254         }
255         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
256         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
257         ++st->numFigs;
258
259         /*  */
260         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
261         rx = mx / 2;
262         ry = my / 2;
263         for (q = 0; q < st->numPoints; q++) {
264             st->a[st->numFigs][q].x = mx / 2 + rx * sin(2 * TWO_PI * (double) q / mp);
265             st->a[st->numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
266         }
267         st->a[st->numFigs][mp].x = st->a[st->numFigs][0].x;
268         st->a[st->numFigs][mp].y = st->a[st->numFigs][0].y;
269         ++st->numFigs;
270     }
271
272     if (st->figType & FT_OPEN) {
273         /* sine wave, one period */
274         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
275         for (q = 0; q < st->numPoints; q++) {
276             st->a[st->numFigs][q].x = ((double) q / st->numPoints) * mx;
277             st->a[st->numFigs][q].y = (1.0 - sin(((double) q / mp) * TWO_PI))
278                 * my / 2.0;
279         }
280         ++st->numFigs;
281
282         /*  */
283         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
284         for (q = 0; q < st->numPoints; q++) {
285             st->a[st->numFigs][q].x = ((double) q / mp) * mx;
286             st->a[st->numFigs][q].y = (1.0 - cos(((double) q / mp) * 3 * TWO_PI))
287                 * my / 2.0;
288         }
289         ++st->numFigs;
290
291         /* spiral, one endpoint at bottom */
292         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
293         rx = mx / 2;
294         ry = my / 2;
295         for (q = 0; q < st->numPoints; q++) {
296             st->a[st->numFigs][q].x = mx / 2 + ry * sin(5 * TWO_PI * (double) q / mp)
297                 * ((double) q / mp);
298             st->a[st->numFigs][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp)
299                 * ((double) q / mp);
300         }
301         ++st->numFigs;
302
303         /* spiral, one endpoint at top */
304         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
305         rx = mx / 2;
306         ry = my / 2;
307         for (q = 0; q < st->numPoints; q++) {
308             st->a[st->numFigs][q].x = mx / 2 + ry * sin(6 * TWO_PI * (double) q / mp)
309                 * ((double) q / mp);
310             st->a[st->numFigs][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp)
311                 * ((double) q / mp);
312         }
313         ++st->numFigs;
314
315         /*  */
316         st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
317         for (q = 0; q < st->numPoints; q++) {
318             st->a[st->numFigs][q].x = ((double) q / mp) * mx;
319             st->a[st->numFigs][q].y = (1.0 - sin(((double) q / mp) * 5 * TWO_PI))
320                 * my / 2.0;
321         }
322         ++st->numFigs;
323     }
324
325 #ifdef MARGINS
326     /* make some space around the figures.  */
327     marginx = (mx + 1) / 10;
328     marginy = (my + 1) / 10;
329     scalex = (double) ((mx + 1) - 2.0 * marginx) / (mx + 1.0);
330     scaley = (double) ((my + 1) - 2.0 * marginy) / (my + 1.0);
331     for (q = 0; q < st->numFigs; q++)
332         for (w = 0; w < st->numPoints; w++) {
333             st->a[q][w].x = marginx + st->a[q][w].x * scalex;
334             st->a[q][w].y = marginy + st->a[q][w].y * scaley;
335         }
336 #endif
337 }
338
339 static void
340 initLMorph(struct state *st)
341 {
342     int               steps;
343     XGCValues         gcv;
344     XWindowAttributes wa;
345     Colormap          cmap;
346     char              *ft;
347     int               i;
348
349     st->maxGamma = 1.0;
350     st->numPoints = get_integer_resource(st->dpy, "points", "Integer");
351     steps = get_integer_resource(st->dpy, "steps", "Integer");
352     st->delay = get_integer_resource(st->dpy, "delay", "Integer");
353     ft = get_string_resource(st->dpy, "figtype", "String");
354
355     if (strcmp(ft, "all") == 0)
356         st->figType = FT_ALL;
357     else if (strcmp(ft, "open") == 0)
358         st->figType = FT_OPEN;
359     else if (strcmp(ft, "closed") == 0)
360         st->figType = FT_CLOSED;
361     else {
362         fprintf(stderr, "figtype should be `all', `open' or `closed'.\n");
363         st->figType = FT_ALL;
364     }
365
366     if (steps <= 0)
367       steps = (random() % 400) + 100;
368
369     st->deltaGamma = 1.0 / steps;
370     XGetWindowAttributes(st->dpy, st->window, &wa);
371     st->scrWidth = wa.width;
372     st->scrHeight = wa.height;
373     cmap = wa.colormap;
374     gcv.foreground = get_pixel_resource(st->dpy, cmap, "foreground", "Foreground");
375     st->gcDraw = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
376     XSetForeground(st->dpy, st->gcDraw, gcv.foreground);
377     gcv.foreground = get_pixel_resource(st->dpy, cmap, "background", "Background");
378     st->gcClear = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
379     XClearWindow(st->dpy, st->window);
380
381     initPointArrays(st);
382     st->aCurr = st->aWork[st->nWork = 0];
383     st->aPrev = NULL;
384     st->currGamma = st->maxGamma + 1.0;  /* force creation of new figure at startup */
385     st->nTo = RND(st->numFigs);
386     do {
387         st->nNext = RND(st->numFigs);
388     } while (st->nNext == st->nTo);
389
390     st->aSlopeTo = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint)); 
391     st->aSlopeFrom = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint)); 
392     st->aNext = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint)); 
393
394     for (i = 0; i < st->numPoints ; i++) {
395         st->aSlopeTo[i].x = 0.0;
396         st->aSlopeTo[i].y = 0.0; 
397     }
398
399     {   /* jwz for version 2.11 */
400         /*      int width = random() % 10;*/
401         int width = get_integer_resource(st->dpy, "linewidth", "Integer");
402         int style = LineSolid;
403         int cap   = (width > 1 ? CapRound  : CapButt);
404         int join  = (width > 1 ? JoinRound : JoinBevel);
405         if (width == 1)
406             width = 0;
407         XSetLineAttributes(st->dpy, st->gcDraw,  width, style, cap, join);
408         XSetLineAttributes(st->dpy, st->gcClear, width, style, cap, join);
409     }
410 }
411
412 /* 55% of execution time */
413 static void
414 createPoints(struct state *st)
415 {
416     int    q;
417     XPoint *pa = st->aCurr, *pa1 = st->aFrom, *pa2 = st->aTo;
418     XPoint *qa1 = st->aSlopeFrom, *qa2 = st->aSlopeTo; 
419     float  fg, f1g;
420     float  speed;
421
422     fg  = st->currGamma;
423     f1g = 1.0 - st->currGamma;
424     for (q = st->numPoints; q; q--) {
425         speed = 0.45 * sin(TWO_PI * (double) (q + st->shift) / (st->numPoints - 1));
426         fg = st->currGamma + 1.67 * speed
427             * exp(-200.0 * (st->currGamma - 0.5 + 0.7 * speed)
428                   * (st->currGamma - 0.5 + 0.7 * speed));
429
430         f1g = 1.0 - fg;
431         pa->x = (short) (f1g * f1g * f1g * pa1->x + f1g * f1g * fg
432                          * (3 * pa1->x + qa1->x) + f1g * fg * fg
433                          * (3 * pa2->x - qa2->x) + fg * fg * fg * pa2->x);
434         pa->y = (short) (f1g * f1g * f1g * pa1->y + f1g * f1g * fg
435                          * (3 * pa1->y + qa1->y) + f1g * fg * fg
436                          * (3 * pa2->y - qa2->y) + fg * fg * fg * pa2->y);
437
438         ++pa;
439         ++pa1;
440         ++pa2;
441         ++qa1;
442         ++qa2;
443     }
444 }
445
446 /* 36% of execution time */
447 static void
448 drawImage(struct state *st)
449 {
450 #if 0
451     int    q;
452     XPoint *old0, *old1, *new0, *new1;
453
454     /* Problem: update the window without too much flickering. I do
455      * this by handling each linesegment separately. First remove a
456      * line, then draw the new line. The problem is that this leaves
457      * small black pixels on the figure. To fix this, we draw the
458      * entire figure using XDrawLines() afterwards. */
459     if (st->aPrev) {
460         old0 = st->aPrev;
461         old1 = st->aPrev + 1;
462         new0 = st->aCurr;
463         new1 = st->aCurr + 1;
464         for (q = st->numPoints - 1; q; q--) {
465            XDrawLine(st->dpy, st->window, st->gcClear,
466              old0->x, old0->y, old1->x, old1->y); 
467             XDrawLine(st->dpy, st->window, st->gcDraw,
468                       new0->x, new0->y, new1->x, new1->y);
469             ++old0;
470             ++old1;
471             ++new0;
472             ++new1;
473         }
474     }
475 #else
476     XClearWindow(st->dpy,st->window);
477 #endif
478     XDrawLines(st->dpy, st->window, st->gcDraw, st->aCurr, st->numPoints, CoordModeOrigin);
479 }
480
481 /* neglectible % of execution time */
482 static void
483 animateLMorph(struct state *st)
484 {
485     int i;
486     if (st->currGamma > st->maxGamma) {
487         st->currGamma = 0.0;
488         st->nFrom = st->nTo;
489         st->nTo = st->nNext;
490         st->aFrom = st->a[st->nFrom];
491         st->aTo = st->a[st->nTo];
492         do {
493             st->nNext = RND(st->numFigs);
494         } while (st->nNext == st->nTo);
495         st->aNext = st->a[st->nNext];
496
497         st->shift = RND(st->numPoints);
498         if (RND(2)) {
499             /* reverse the array to get more variation. */
500             int    i1, i2;
501             XPoint p;
502             
503             for (i1 = 0, i2 = st->numPoints - 1; i1 < st->numPoints / 2; i1++, i2--) {
504                 p = st->aNext[i1];
505                 st->aNext[i1] = st->aNext[i2];
506                 st->aNext[i2] = p;
507             }
508         }
509
510         /* calculate the slopes */
511         for (i = 0; i < st->numPoints ; i++) {
512             st->aSlopeFrom[i].x = st->aSlopeTo[i].x;
513             st->aSlopeFrom[i].y = st->aSlopeTo[i].y;
514             st->aSlopeTo[i].x = st->aNext[i].x - st->aTo[i].x;
515             st->aSlopeTo[i].y = (st->aNext[i].y - st->aTo[i].y);
516         }
517     }
518
519     createPoints(st);
520     drawImage(st);
521     st->aPrev = st->aCurr;
522     st->aCurr = st->aWork[st->nWork ^= 1];
523
524     st->currGamma += st->deltaGamma;
525 }
526
527 /*-----------------------------------------------------------------------+
528 |  PUBLIC FUNCTIONS                                                      |
529 +-----------------------------------------------------------------------*/
530
531 static void *
532 lmorph_init (Display *d, Window w)
533 {
534   struct state *st = (struct state *) calloc (1, sizeof(*st));
535   st->dpy = d;
536   st->window = w;
537   initLMorph(st);
538   return st;
539 }
540
541 static unsigned long
542 lmorph_draw (Display *dpy, Window window, void *closure)
543 {
544   struct state *st = (struct state *) closure;
545   animateLMorph(st);
546   return st->delay;
547 }
548
549 static void
550 lmorph_reshape (Display *dpy, Window window, void *closure, 
551                  unsigned int w, unsigned int h)
552 {
553 }
554
555 static Bool
556 lmorph_event (Display *dpy, Window window, void *closure, XEvent *event)
557 {
558   return False;
559 }
560
561 static void
562 lmorph_free (Display *dpy, Window window, void *closure)
563 {
564   struct state *st = (struct state *) closure;
565   free (st);
566 }
567
568 XSCREENSAVER_MODULE ("LMorph", lmorph)