1 /* lmorph, Copyright (c) 1993-1999 Sverre H. Huseby and Glenn T. Lines
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
12 /*------------------------------------------------------------------------
15 | MODULE OF xscreensaver
17 | DESCRIPTION Smooth and non-linear morphing between 1D curves.
19 | WRITTEN BY Sverre H. Huseby Glenn T. Lines
20 | Kurvn. 30 Østgaardsgt. 5
21 | N-0495 Oslo N-0474 Oslo
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/
28 | The original idea, and the bilinear interpolation
29 | mathematics used, emerged in the head of the wise
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).
41 | * Added cubic interpolation between shapes
42 | * Added non-linear transformation speed
45 | * Minor code cleanup.
48 | * Some code reformatting.
49 | * Added possibility to use float arithmetic.
50 | * Added -figtype option.
51 | * Made color blue default.
54 | * Function headers converted from ANSI to K&R.
55 | * Added posibility for random number of steps, and
56 | made this the default.
59 | * Converted from an MS-Windows program to X Window.
61 | november 1993 (gtl, shh, lots of beer)
62 | * Original Windows version (we didn't know better).
63 +----------------------------------------------------------------------*/
66 #include "screenhack.h"
68 /*-----------------------------------------------------------------------+
70 +-----------------------------------------------------------------------*/
72 /* define MARGINS to make some space around the figure. */
76 #define TWO_PI (2.0 * M_PI)
77 #define RND(x) (random() % (x))
81 #define FT_ALL (FT_OPEN | FT_CLOSED)
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 */
96 long delay; /* usecs to wait between updates. */
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 */
109 int scrWidth, scrHeight;
110 double currGamma, maxGamma, deltaGamma;
115 /*-----------------------------------------------------------------------+
117 +-----------------------------------------------------------------------*/
119 static const char *lmorph_defaults [] = {
120 ".background: black",
121 ".foreground: #4444FF",
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 },
139 /*-----------------------------------------------------------------------+
140 | PRIVATE FUNCTIONS |
141 +-----------------------------------------------------------------------*/
148 if ((ret = malloc(size)) == NULL) {
149 fprintf(stderr, "lmorph: out of memory\n");
156 initPointArrays(struct state *st)
159 int mx, my; /* max screen coordinates. */
160 int mp; /* max point number. */
162 int marginx, marginy;
163 double scalex, scaley;
165 mx = st->scrWidth - 1;
166 my = st->scrHeight - 1;
167 mp = st->numPoints - 1;
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));
173 if (st->figType & FT_CLOSED) {
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;
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;
194 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
206 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
218 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
235 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
248 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
260 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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);
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;
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))
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))
291 /* spiral, one endpoint at bottom */
292 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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)
298 st->a[st->numFigs][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp)
303 /* spiral, one endpoint at top */
304 st->a[st->numFigs] = (XPoint *) xmalloc(st->numPoints * sizeof(XPoint));
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)
310 st->a[st->numFigs][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp)
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))
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;
340 initLMorph(struct state *st)
344 XWindowAttributes wa;
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");
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;
362 fprintf(stderr, "figtype should be `all', `open' or `closed'.\n");
363 st->figType = FT_ALL;
367 steps = (random() % 400) + 100;
369 st->deltaGamma = 1.0 / steps;
370 XGetWindowAttributes(st->dpy, st->window, &wa);
371 st->scrWidth = wa.width;
372 st->scrHeight = wa.height;
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);
382 st->aCurr = st->aWork[st->nWork = 0];
384 st->currGamma = st->maxGamma + 1.0; /* force creation of new figure at startup */
385 st->nTo = RND(st->numFigs);
387 st->nNext = RND(st->numFigs);
388 } while (st->nNext == st->nTo);
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));
394 for (i = 0; i < st->numPoints ; i++) {
395 st->aSlopeTo[i].x = 0.0;
396 st->aSlopeTo[i].y = 0.0;
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);
407 XSetLineAttributes(st->dpy, st->gcDraw, width, style, cap, join);
408 XSetLineAttributes(st->dpy, st->gcClear, width, style, cap, join);
412 /* 55% of execution time */
414 createPoints(struct state *st)
417 XPoint *pa = st->aCurr, *pa1 = st->aFrom, *pa2 = st->aTo;
418 XPoint *qa1 = st->aSlopeFrom, *qa2 = st->aSlopeTo;
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));
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);
446 /* 36% of execution time */
448 drawImage(struct state *st)
452 XPoint *old0, *old1, *new0, *new1;
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. */
461 old1 = st->aPrev + 1;
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);
476 XClearWindow(st->dpy,st->window);
478 XDrawLines(st->dpy, st->window, st->gcDraw, st->aCurr, st->numPoints, CoordModeOrigin);
481 /* neglectible % of execution time */
483 animateLMorph(struct state *st)
486 if (st->currGamma > st->maxGamma) {
490 st->aFrom = st->a[st->nFrom];
491 st->aTo = st->a[st->nTo];
493 st->nNext = RND(st->numFigs);
494 } while (st->nNext == st->nTo);
495 st->aNext = st->a[st->nNext];
497 st->shift = RND(st->numPoints);
499 /* reverse the array to get more variation. */
503 for (i1 = 0, i2 = st->numPoints - 1; i1 < st->numPoints / 2; i1++, i2--) {
505 st->aNext[i1] = st->aNext[i2];
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);
521 st->aPrev = st->aCurr;
522 st->aCurr = st->aWork[st->nWork ^= 1];
524 st->currGamma += st->deltaGamma;
527 /*-----------------------------------------------------------------------+
529 +-----------------------------------------------------------------------*/
532 lmorph_init (Display *d, Window w)
534 struct state *st = (struct state *) calloc (1, sizeof(*st));
542 lmorph_draw (Display *dpy, Window window, void *closure)
544 struct state *st = (struct state *) closure;
550 lmorph_reshape (Display *dpy, Window window, void *closure,
551 unsigned int w, unsigned int h)
556 lmorph_event (Display *dpy, Window window, void *closure, XEvent *event)
562 lmorph_free (Display *dpy, Window window, void *closure)
564 struct state *st = (struct state *) closure;
568 XSCREENSAVER_MODULE ("LMorph", lmorph)