http://ftp.x.org/contrib/applications/xscreensaver-2.17.tar.gz
[xscreensaver] / hacks / flow.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* flow --- flow of strange bees */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Sun.COM>
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * "flow" shows a variety of continuous phase-space flows around strange
25  * attractors.  It includes the well-known Lorentz mask (the "Butterfly"
26  * of chaos fame), two forms of Rossler's "Folded Band" and a Poincare'
27  * section of the "Bagel".
28  *
29  * Revision History:
30  * 09-Apr-97: Ported to xlockmore-4
31  * 18-Jul-96: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
32  * 31-Aug-90: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
33  */
34
35 #ifdef STANDALONE
36 # define PROGCLASS      "Flow"
37 # define HACK_INIT      init_flow
38 # define HACK_DRAW      draw_flow
39 # define flow_opts      xlockmore_opts
40 # define DEFAULTS       "*delay:                1000 \n" \
41                                         "*count:                1024 \n" \
42                                         "*cycles:               3000 \n" \
43                                         "*ncolors:              200 \n"
44 # define SMOOTH_COLORS
45 # include "xlockmore.h"         /* in xscreensaver distribution */
46 # include "erase.h"
47
48 #else /* STANDALONE */
49 # include "xlock.h"             /* in xlockmore distribution */
50 #endif /* STANDALONE */
51
52 ModeSpecOpt flow_opts =
53 {0, NULL, 0, NULL, NULL};
54
55 #ifdef USE_MODULES
56 ModStruct   flow_description =
57 {"flow", "init_flow", "draw_flow", "release_flow",
58  "refresh_flow", "init_flow", NULL, &flow_opts,
59  1000, 1024, 3000, 1, 64, 1.0, "",
60  "Shows dynamic strange attractors", 0, NULL};
61
62 #endif
63
64 #define TIMES   2               /* number of time positions recorded */
65
66 typedef struct {
67         double      x;
68         double      y;
69         double      z;
70 } dvector;
71
72 typedef struct {
73         double      a, b, c;
74 } Par;
75
76 /* Macros */
77 #define X(t,b)  (sp->p[(t)*sp->beecount+(b)].x)
78 #define Y(t,b)  (sp->p[(t)*sp->beecount+(b)].y)
79 #define Z(t,b)  (sp->p[(t)*sp->beecount+(b)].z)
80 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2))         /* random number around 0 */
81
82 typedef struct {
83         int         pix;
84         int         width;
85         int         height;
86         int         count;
87         double      size;
88
89         int         beecount;   /* number of bees */
90         XSegment   *csegs;      /* bee lines */
91         int        *cnsegs;
92         XSegment   *old_segs;   /* old bee lines */
93         double      step;
94         dvector     c;          /* centre */
95         dvector    *p;          /* bee positions x[time][bee#] */
96         double     *t;
97         double      theta;
98         double      dtheta;
99         double      phi;
100         double      dphi;
101         void        (*ODE) (Par par,
102                                                 double *dx, double *dy, double *dz,
103                                                 double x,   double y,   double z,
104                                                 double t);
105         Par         par;
106 } flowstruct;
107
108 static flowstruct *flows = NULL;
109
110 static void
111 Lorentz(Par par,
112                 double *dx, double *dy, double *dz,
113                 double x,   double y,   double z,
114                 double t)
115 {
116         *dx = par.a * (y - x);
117         *dy = x * (par.b - z) - y;
118         *dz = x * y - par.c * z;
119 }
120
121 static void
122 Rossler(Par par,
123                 double *dx, double *dy, double *dz,
124                 double x,   double y,   double z,
125                 double t)
126 {
127         *dx = -(y + par.a * z);
128         *dy = x + y * par.b;
129         *dz = par.c + z * (x - 5.7);
130 }
131
132 static void
133 RosslerCone(Par par,
134                         double *dx, double *dy, double *dz,
135                         double x,   double y,   double z,
136                         double t)
137 {
138         *dx = -(y + par.a * z);
139         *dy = x + y * par.b - z * z * par.c;
140         *dz = 0.2 + z * (x - 5.7);
141 }
142
143 static void
144 Bagel(Par par,
145           double *dx, double *dy, double *dz,
146           double x,   double y,   double z,
147           double t)
148 {
149         *dx = -y + par.b * sin(par.c * t);
150         *dy = 0.7 * x + par.a * y * (0.1 - x * x);
151         *dz = 0;
152 }
153
154 void
155 init_flow(ModeInfo * mi)
156 {
157         flowstruct *sp;
158         int         b;
159         dvector     range;
160
161         if (flows == NULL) {
162                 if ((flows = (flowstruct *) calloc(MI_NUM_SCREENS(mi),
163                                                sizeof (flowstruct))) == NULL)
164                         return;
165         }
166         sp = &flows[MI_SCREEN(mi)];
167
168         sp->beecount = MI_COUNT(mi);
169         if (sp->beecount < 0) {
170                 /* if sp->beecount is random ... the size can change */
171                 if (sp->csegs != NULL) {
172                         (void) free((void *) sp->csegs);
173                         sp->csegs = NULL;
174                 }
175                 if (sp->cnsegs != NULL) {
176                         (void) free((void *) sp->cnsegs);
177                         sp->cnsegs = NULL;
178                 }
179                 if (sp->old_segs != NULL) {
180                         (void) free((void *) sp->old_segs);
181                         sp->old_segs = NULL;
182                 }
183                 if (sp->p != NULL) {
184                         (void) free((void *) sp->p);
185                         sp->p = NULL;
186                 }
187                 if (sp->t != NULL) {
188                         (void) free((void *) sp->t);
189                         sp->t = NULL;
190                 }
191                 sp->beecount = NRAND(-sp->beecount) + 1;        /* Add 1 so its not too boring */
192         }
193         sp->count = 0;
194
195         sp->width = MI_WIDTH(mi);
196         sp->height = MI_HEIGHT(mi);
197
198         sp->theta = balance_rand(M_PI);
199         sp->phi = balance_rand(M_PI);
200         sp->dtheta = 0.002;
201         sp->dphi = 0.001;
202         switch (NRAND(4)) {
203                 case 0:
204                         sp->ODE = Lorentz;
205                         sp->step = 0.02;
206                         sp->size = 60;
207                         sp->c.x = 0;
208                         sp->c.y = 0;
209                         sp->c.z = 24;
210                         range.x = 5;
211                         range.y = 5;
212                         range.z = 1;
213                         sp->par.a = 10 + balance_rand(5);
214                         sp->par.b = 28 + balance_rand(5);
215                         sp->par.c = 2 + balance_rand(1);
216                         break;
217                 case 1:
218                         sp->ODE = Rossler;
219                         sp->step = 0.05;
220                         sp->size = 24;
221                         sp->c.x = 0;
222                         sp->c.y = 0;
223                         sp->c.z = 3;
224                         range.x = 4;
225                         range.y = 4;
226                         range.z = 7;
227                         sp->par.a = 2 + balance_rand(1);
228                         sp->par.b = 0.2 + balance_rand(0.1);
229                         sp->par.c = 0.2 + balance_rand(0.1);
230                         break;
231                 case 2:
232                         sp->ODE = RosslerCone;
233                         sp->step = 0.05;
234                         sp->size = 24;
235                         sp->c.x = 0;
236                         sp->c.y = 0;
237                         sp->c.z = 3;
238                         range.x = 4;
239                         range.y = 4;
240                         range.z = 4;
241                         sp->par.a = 2;
242                         sp->par.b = 0.2;
243                         sp->par.c = 0.25 + balance_rand(0.09);
244                         break;
245                 case 3:
246                 default:
247                         sp->ODE = Bagel;
248                         sp->step = 0.04;
249                         sp->size = 2.6;
250                         sp->c.x = 0 /*-1.0*/ ;
251                         sp->c.y = 0;
252                         sp->c.z = 0;
253                         range.x = 3;
254                         range.y = 4;
255                         range.z = 0;
256                         sp->par.a = 10 + balance_rand(5);
257                         sp->par.b = 0.35 + balance_rand(0.25);
258                         sp->par.c = 1.57;
259                         sp->theta = 0;
260                         sp->phi = 0;
261                         sp->dtheta = 0 /*sp->par.c*sp->step */ ;
262                         sp->dphi = 0;
263                         break;
264         }
265
266         /* Clear the background. */
267         MI_CLEARWINDOW(mi);
268
269         /* Allocate memory. */
270
271         if (!sp->csegs) {
272                 sp->csegs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount
273                                                 * MI_NPIXELS(mi));
274                 sp->cnsegs = (int *) malloc(sizeof (int) * MI_NPIXELS(mi));
275
276                 sp->old_segs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount);
277                 sp->p = (dvector *) malloc(sizeof (dvector) * sp->beecount * TIMES);
278                 sp->t = (double *) malloc(sizeof (double) * sp->beecount);
279         }
280         /* Initialize point positions, velocities, etc. */
281
282         /* bees */
283         for (b = 0; b < sp->beecount; b++) {
284                 X(0, b) = balance_rand(range.x);
285                 X(1, b) = X(0, b);
286                 Y(0, b) = balance_rand(range.y);
287                 Y(1, b) = Y(0, b);
288                 Z(0, b) = balance_rand(range.z);
289                 Z(1, b) = Z(0, b);
290                 sp->t[b] = 0;
291         }
292 }
293
294
295 void
296 draw_flow(ModeInfo * mi)
297 {
298         Display    *display = MI_DISPLAY(mi);
299         Window      window = MI_WINDOW(mi);
300         GC          gc = MI_GC(mi);
301         flowstruct *sp = &flows[MI_SCREEN(mi)];
302         int         b, c;
303         int         col, ix;
304         double      sint, cost, sinp, cosp;
305
306         sp->theta += sp->dtheta;
307         sp->phi += sp->dphi;
308         sint = sin(sp->theta);
309         cost = cos(sp->theta);
310         sinp = sin(sp->phi);
311         cosp = cos(sp->phi);
312         for (col = 0; col < MI_NPIXELS(mi); col++)
313                 sp->cnsegs[col] = 0;
314
315         /* <=- Bees -=> */
316         for (b = 0; b < sp->beecount; b++) {
317                 /* Age the arrays. */
318                 X(1, b) = X(0, b);
319                 Y(1, b) = Y(0, b);
320                 Z(1, b) = Z(0, b);
321
322                 /* 2nd order Kunge Kutta */
323                 {
324                         double      k1x, k1y, k1z;
325                         double      k2x, k2y, k2z;
326
327                         sp->t[b] += sp->step;   /* tick */
328                         sp->ODE(sp->par, &k1x, &k1y, &k1z,
329                                 X(1, b), Y(1, b), Z(1, b), sp->t[b]);
330                         k1x *= sp->step;
331                         k1y *= sp->step;
332                         k1z *= sp->step;
333                         sp->ODE(sp->par, &k2x, &k2y, &k2z,
334                                 X(1, b) + k1x, Y(1, b) + k1y, Z(1, b) + k1z, sp->t[b]);
335                         k2x *= sp->step;
336                         k2y *= sp->step;
337                         k2z *= sp->step;
338                         X(0, b) = X(1, b) + (k1x + k2x) / 2.0;
339                         Y(0, b) = Y(1, b) + (k1y + k2y) / 2.0;
340                         Z(0, b) = Z(1, b) + (k1z + k2z) / 2.0;
341                 }
342
343                 /* Fill the segment lists. */
344
345
346                 /* Tumble */
347 #define DISPLAYX(A) (sp->width/2+sp->width/sp->size* \
348                      ((X((A),b)-sp->c.x)*cost \
349                       -(Y((A),b)-sp->c.y)*sint*cosp \
350                       +(Z((A),b)-sp->c.z)*sint*sinp))
351 #define DISPLAYY(A) (sp->height/2-sp->height/sp->size* \
352                      ((X((A),b)-sp->c.x)*sint \
353                       +(Y((A),b)-sp->c.y)*cost*cosp \
354                       -(Z((A),b)-sp->c.z)*cost*sinp))
355
356                 /* Colour according to bee */
357                 col = b % (MI_NPIXELS(mi) - 1);
358
359                 ix = col * sp->beecount + sp->cnsegs[col];
360                 sp->csegs[ix].x1 = DISPLAYX(0);
361                 sp->csegs[ix].y1 = DISPLAYY(0);
362                 sp->csegs[ix].x2 = DISPLAYX(1);
363                 sp->csegs[ix].y2 = DISPLAYY(1);
364                 sp->cnsegs[col]++;
365         }
366         if (sp->count) {
367                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
368                 XDrawSegments(display, window, gc, sp->old_segs, sp->beecount);
369         }
370         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
371         if (MI_NPIXELS(mi) > 2) {
372                 for (col = 0; col < MI_NPIXELS(mi); col++) {
373                         if (sp->cnsegs[col] > 0) {
374                                 XSetForeground(display, gc, MI_PIXEL(mi, col));
375                                 XDrawSegments(display, window, gc,
376                                               sp->csegs + col * sp->beecount,
377                                               sp->cnsegs[col]);
378                         }
379                 }
380         } else {
381           /* mono */
382           XSetForeground(display, gc, MI_PIXEL(mi, 1));
383           XDrawSegments(display, window, gc,
384                                         sp->csegs + col * sp->beecount,
385                                         sp->cnsegs[col]);
386         }
387         for (col = 0, c = 0; col < MI_NPIXELS(mi); col++)
388                 for (b = 0; b < sp->cnsegs[col]; b++) {
389                         XSegment    s = (sp->csegs + col * sp->beecount)[b];
390
391                         sp->old_segs[c].x1 = s.x1;
392                         sp->old_segs[c].y1 = s.y1;
393                         sp->old_segs[c].x2 = s.x2;
394                         sp->old_segs[c].y2 = s.y2;
395                         c++;
396                 }
397         if (++sp->count > MI_CYCLES(mi)) {
398                 init_flow(mi);
399         }
400 }
401
402 void
403 release_flow(ModeInfo * mi)
404 {
405         if (flows != NULL) {
406                 int         screen;
407
408                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
409                         flowstruct *sp = &flows[screen];
410
411                         if (sp->csegs != NULL)
412                                 (void) free((void *) sp->csegs);
413                         if (sp->cnsegs != NULL)
414                                 (void) free((void *) sp->cnsegs);
415                         if (sp->old_segs != NULL)
416                                 (void) free((void *) sp->old_segs);
417                         if (sp->p != NULL)
418                                 (void) free((void *) sp->p);
419                         if (sp->t != NULL)
420                                 (void) free((void *) sp->t);
421                 }
422                 (void) free((void *) flows);
423                 flows = NULL;
424         }
425 }
426
427 void
428 refresh_flow(ModeInfo * mi)
429 {
430         MI_CLEARWINDOW(mi);
431 }