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 +----------------------------------------------------------------------*/
69 #include "screenhack.h"
71 /*-----------------------------------------------------------------------+
73 +-----------------------------------------------------------------------*/
75 /* define MARGINS to make some space around the figure. */
79 #define TWO_PI (2.0 * M_PI)
80 #define RND(x) (random() % (x))
84 #define FT_ALL (FT_OPEN | FT_CLOSED)
87 numFigs = 0, /* number of figure arrays. */
88 numPoints, /* number of points in each array. */
89 nWork, /* current work array number. */
90 nFrom, /* current from array number. */
91 nTo, /* current to array number. */
92 nNext, /* current next array number (after to).*/
93 shift, /* shifts the starting point of a figure */
95 static long delay; /* usecs to wait between updates. */
97 *aWork[2], /* working arrays. */
98 *a[MAXFIGS], /* the figure arrays. */
99 *aTmp, /* used as source when interrupting morph */
100 *aPrev, /* previous points displayed. */
101 *aCurr, /* the current points displayed. */
102 *aFrom, /* figure converting from. */
103 *aTo, /* figure converting to. */
104 *aNext, /* figure converting to next time. */
105 *aSlopeFrom, /* slope at start of morph */
106 *aSlopeTo; /* slope at end of morph */
107 static int scrWidth, scrHeight;
108 static double currGamma, maxGamma = 1.0, deltaGamma;
109 static GC gcDraw, gcClear;
111 static Window window;
113 /*-----------------------------------------------------------------------+
115 +-----------------------------------------------------------------------*/
117 char *progclass = "LMorph";
119 char *defaults [] = {
120 ".background: black",
121 ".foreground: #4444FF",
130 XrmOptionDescRec 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 },
138 int options_size = (sizeof (options) / sizeof (options[0]));
140 /*-----------------------------------------------------------------------+
141 | PRIVATE FUNCTIONS |
142 +-----------------------------------------------------------------------*/
149 if ((ret = malloc(size)) == NULL) {
150 fprintf(stderr, "lmorph: out of memory\n");
157 initPointArrays(void)
160 int mx, my; /* max screen coordinates. */
161 int mp; /* max point number. */
163 int marginx, marginy;
164 double scalex, scaley;
170 aWork[0] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
171 aWork[1] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
172 aTmp = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
174 if (figType & FT_CLOSED) {
176 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
178 for (q = 0; q < s; q++) {
179 a[numFigs][q].x = ((double) q / s) * mx;
181 a[numFigs][s + q].x = mx;
182 a[numFigs][s + q].y = ((double) q / s) * my;
183 a[numFigs][2 * s + q].x = mx - ((double) q / s) * mx;
184 a[numFigs][2 * s + q].y = my;
185 a[numFigs][3 * s + q].x = 0;
186 a[numFigs][3 * s + q].y = my - ((double) q / s) * my;
188 for (q = 4 * s; q < numPoints; q++)
189 a[numFigs][q].x = a[numFigs][q].y = 0;
190 a[numFigs][mp].x = a[numFigs][0].x;
191 a[numFigs][mp].y = a[numFigs][0].y;
195 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
198 for (q = 0; q < numPoints; q++) {
199 a[numFigs][q].x = mx / 2 + rx * sin(1 * TWO_PI * (double) q / mp);
200 a[numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
202 a[numFigs][mp].x = a[numFigs][0].x;
203 a[numFigs][mp].y = a[numFigs][0].y;
207 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
210 for (q = 0; q < numPoints; q++) {
211 a[numFigs][q].x = mx / 2 + ry * sin(3 * TWO_PI * (double) q / mp);
212 a[numFigs][q].y = my / 2 + ry * cos(1 * TWO_PI * (double) q / mp);
214 a[numFigs][mp].x = a[numFigs][0].x;
215 a[numFigs][mp].y = a[numFigs][0].y;
219 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
222 for (q = 0; q < numPoints; q++) {
223 a[numFigs][q].x = mx / 2 + ry
224 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
225 * sin(TWO_PI * (double) q / mp);
226 a[numFigs][q].y = my / 2 + ry
227 * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp))
228 * cos(TWO_PI * (double) q / mp);
230 a[numFigs][mp].x = a[numFigs][0].x;
231 a[numFigs][mp].y = a[numFigs][0].y;
236 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
239 for (q = 0; q < numPoints; q++) {
240 a[numFigs][q].x = mx / 2 + ry * sin(TWO_PI * (double) q / mp);
241 a[numFigs][q].y = my / 2 + ry * cos(TWO_PI * (double) q / mp);
243 a[numFigs][mp].x = a[numFigs][0].x;
244 a[numFigs][mp].y = a[numFigs][0].y;
249 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
252 for (q = 0; q < numPoints; q++) {
253 a[numFigs][q].x = mx / 2 + rx * cos(TWO_PI * (double) q / mp);
254 a[numFigs][q].y = my / 2 + ry * sin(TWO_PI * (double) q / mp);
256 a[numFigs][mp].x = a[numFigs][0].x;
257 a[numFigs][mp].y = a[numFigs][0].y;
261 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
264 for (q = 0; q < numPoints; q++) {
265 a[numFigs][q].x = mx / 2 + rx * sin(2 * TWO_PI * (double) q / mp);
266 a[numFigs][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp);
268 a[numFigs][mp].x = a[numFigs][0].x;
269 a[numFigs][mp].y = a[numFigs][0].y;
273 if (figType & FT_OPEN) {
274 /* sine wave, one period */
275 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
276 for (q = 0; q < numPoints; q++) {
277 a[numFigs][q].x = ((double) q / numPoints) * mx;
278 a[numFigs][q].y = (1.0 - sin(((double) q / mp) * TWO_PI))
284 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
285 for (q = 0; q < numPoints; q++) {
286 a[numFigs][q].x = ((double) q / mp) * mx;
287 a[numFigs][q].y = (1.0 - cos(((double) q / mp) * 3 * TWO_PI))
292 /* spiral, one endpoint at bottom */
293 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
296 for (q = 0; q < numPoints; q++) {
297 a[numFigs][q].x = mx / 2 + ry * sin(5 * TWO_PI * (double) q / mp)
299 a[numFigs][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp)
304 /* spiral, one endpoint at top */
305 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
308 for (q = 0; q < numPoints; q++) {
309 a[numFigs][q].x = mx / 2 + ry * sin(6 * TWO_PI * (double) q / mp)
311 a[numFigs][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp)
317 a[numFigs] = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
318 for (q = 0; q < numPoints; q++) {
319 a[numFigs][q].x = ((double) q / mp) * mx;
320 a[numFigs][q].y = (1.0 - sin(((double) q / mp) * 5 * TWO_PI))
327 /* make some space around the figures. */
328 marginx = (mx + 1) / 10;
329 marginy = (my + 1) / 10;
330 scalex = (double) ((mx + 1) - 2.0 * marginx) / (mx + 1.0);
331 scaley = (double) ((my + 1) - 2.0 * marginy) / (my + 1.0);
332 for (q = 0; q < numFigs; q++)
333 for (w = 0; w < numPoints; w++) {
334 a[q][w].x = marginx + a[q][w].x * scalex;
335 a[q][w].y = marginy + a[q][w].y * scaley;
345 XWindowAttributes wa;
350 numPoints = get_integer_resource("points", "Integer");
351 steps = get_integer_resource("steps", "Integer");
352 delay = get_integer_resource("delay", "Integer");
353 ft = get_string_resource("figtype", "String");
355 if (strcmp(ft, "all") == 0)
357 else if (strcmp(ft, "open") == 0)
359 else if (strcmp(ft, "closed") == 0)
362 fprintf(stderr, "figtype should be `all', `open' or `closed'.\n");
367 steps = (random() % 400) + 100;
369 deltaGamma = 1.0 / steps;
370 XGetWindowAttributes(dpy, window, &wa);
372 scrHeight = wa.height;
374 gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
375 gcDraw = XCreateGC(dpy, window, GCForeground, &gcv);
376 XSetForeground(dpy, gcDraw, gcv.foreground);
377 gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
378 gcClear = XCreateGC(dpy, window, GCForeground, &gcv);
379 XClearWindow(dpy, window);
382 aCurr = aWork[nWork = 0];
384 currGamma = maxGamma + 1.0; /* force creation of new figure at startup */
387 nNext = RND(numFigs);
388 } while (nNext == nTo);
390 aSlopeTo = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
391 aSlopeFrom = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
392 aNext = (XPoint *) xmalloc(numPoints * sizeof(XPoint));
394 for (i = 0; i < numPoints ; i++) {
399 { /* jwz for version 2.11 */
400 /* int width = random() % 10;*/
401 int width = get_integer_resource("linewidth", "Integer");
402 int style = LineSolid;
403 int cap = (width > 1 ? CapRound : CapButt);
404 int join = (width > 1 ? JoinRound : JoinBevel);
407 XSetLineAttributes(dpy, gcDraw, width, style, cap, join);
408 XSetLineAttributes(dpy, gcClear, width, style, cap, join);
412 /* 55% of execution time */
417 XPoint *pa = aCurr, *pa1 = aFrom, *pa2 = aTo;
418 XPoint *qa1 = aSlopeFrom, *qa2 = aSlopeTo;
423 f1g = 1.0 - currGamma;
424 for (q = numPoints; q; q--) {
425 speed = 0.45 * sin(TWO_PI * (double) (q + shift) / (numPoints - 1));
426 fg = currGamma + 1.67 * speed
427 * exp(-200.0 * (currGamma - 0.5 + 0.7 * speed)
428 * (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 */
451 XPoint *old0, *old1, *new0, *new1;
453 /* Problem: update the window without too much flickering. I do
454 * this by handling each linesegment separately. First remove a
455 * line, then draw the new line. The problem is that this leaves
456 * small black pixels on the figure. To fix this, we draw the
457 * entire figure using XDrawLines() afterwards. */
463 for (q = numPoints - 1; q; q--) {
464 XDrawLine(dpy, window, gcClear,
465 old0->x, old0->y, old1->x, old1->y);
466 XDrawLine(dpy, window, gcDraw,
467 new0->x, new0->y, new1->x, new1->y);
474 XDrawLines(dpy, window, gcDraw, aCurr, numPoints, CoordModeOrigin);
479 /* neglectible % of execution time */
484 if (currGamma > maxGamma) {
491 nNext = RND(numFigs);
492 } while (nNext == nTo);
495 shift = RND(numPoints);
497 /* reverse the array to get more variation. */
501 for (i1 = 0, i2 = numPoints - 1; i1 < numPoints / 2; i1++, i2--) {
503 aNext[i1] = aNext[i2];
508 /* calculate the slopes */
509 for (i = 0; i < numPoints ; i++) {
510 aSlopeFrom[i].x = aSlopeTo[i].x;
511 aSlopeFrom[i].y = aSlopeTo[i].y;
512 aSlopeTo[i].x = aNext[i].x - aTo[i].x;
513 aSlopeTo[i].y = (aNext[i].y - aTo[i].y);
520 aCurr = aWork[nWork ^= 1];
522 currGamma += deltaGamma;
525 /*-----------------------------------------------------------------------+
527 +-----------------------------------------------------------------------*/
530 screenhack(Display *disp, Window win)
537 screenhack_handle_events (dpy);