1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* flow --- flow of strange bees */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
10 * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Sun.COM>
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.
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.
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 Poincare'
27 * sections of the "Birkhoff Bagel" and Duffing's forced occilator.
30 * 31-Nov-98: [TDA] Added Duffing (what a strange day that was :) DAB)
31 * Duffing's forced oscillator has been added to the formula list and
32 * the parameters section has been updated to display it in Poincare'
34 * 30-Nov-98: [TDA] Added travelling perspective option
35 * A more exciting point-of-view has been added to all autonomous flows.
36 * This views the flow as seen by a particle moving with the flow. In the
37 * metaphor of the original code, I've attached a camera to one of the
39 * 30-Nov-98: [TDA] Much code cleanup.
40 * 09-Apr-97: [TDA] Ported to xlockmore-4
41 * 18-Jul-96: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
42 * 31-Aug-90: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
46 # define PROGCLASS "Flow"
47 # define HACK_INIT init_flow
48 # define HACK_DRAW draw_flow
49 # define flow_opts xlockmore_opts
50 # define DEFAULTS "*delay: 1000 \n" \
54 # define SMOOTH_COLORS
55 # include "xlockmore.h" /* in xscreensaver distribution */
58 #else /* STANDALONE */
59 # include "xlock.h" /* in xlockmore distribution */
60 #endif /* STANDALONE */
62 ModeSpecOpt flow_opts = { 0, NULL, 0, NULL, NULL };
65 ModStruct flow_description = {
66 "flow", "init_flow", "draw_flow", "release_flow",
67 "refresh_flow", "init_flow", NULL, &flow_opts,
68 1000, 1024, 3000, 1, 64, 1.0, "",
69 "Shows dynamic strange attractors", 0, NULL
85 #define X(t,b) (sp->p[t][b].x)
86 #define Y(t,b) (sp->p[t][b].y)
87 #define Z(t,b) (sp->p[t][b].z)
88 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
89 #define SCALE_X(A) (sp->width/2+sp->width/sp->size*(A))
90 #define SCALE_Y(A) (sp->height/2+sp->height/sp->size*(A))
98 int beecount; /* number of bees */
99 XSegment *csegs; /* bee lines */
101 XSegment *old_segs; /* old bee lines */
104 dvector centre; /* centre */
109 dvector *p[2]; /* bee positions x[time][bee#] */
116 dvector (*ODE) (Par par, double x, double y, double z);
120 static flowstruct *flows = NULL;
123 Lorentz(Par par, double x, double y, double z)
127 d.x = par.a * (y - x);
128 d.y = x * (par.b - z) - y;
129 d.z = x * y - par.c * z;
134 Rossler(Par par, double x, double y, double z)
138 d.x = -(y + par.a * z);
140 d.z = par.c + z * (x - 5.7);
145 RosslerCone(Par par, double x, double y, double z)
149 d.x = -(y + par.a * z);
150 d.y = x + y * par.b - z * z * par.c;
151 d.z = 0.2 + z * (x - 5.7);
156 Birkhoff(Par par, double x, double y, double z)
160 d.x = -y + par.b * sin(z);
161 d.y = 0.7 * x + par.a * y * (0.1 - x * x);
167 Duffing(Par par, double x, double y, double z)
171 d.x = -par.a * x - y/2 - y * y * y/8 + par.b * cos(z);
178 init_flow(ModeInfo * mi)
184 static int allocated = 0;
187 if ((flows = (flowstruct *) calloc(MI_NUM_SCREENS(mi),
188 sizeof (flowstruct))) == NULL)
191 sp = &flows[MI_SCREEN(mi)];
195 sp->width = MI_WIDTH(mi);
196 sp->height = MI_HEIGHT(mi);
198 sp->tumble.theta = balance_rand(M_PI);
199 sp->tumble.phi = balance_rand(M_PI);
200 sp->tumble.dtheta = 0.002;
201 sp->tumble.dphi = 0.001;
203 sp->view.depth = 0; /* no perspective view */
208 sp->view.height = 0.2;
220 sp->par.a = 10 + balance_rand(5);
221 sp->par.b = 28 + balance_rand(5);
222 sp->par.c = 2 + balance_rand(1);
226 sp->view.height = 0.1;
238 sp->par.a = 2 + balance_rand(1);
239 sp->par.b = 0.2 + balance_rand(0.1);
240 sp->par.c = 0.2 + balance_rand(0.1);
244 sp->view.height = 0.1;
247 sp->ODE = RosslerCone;
258 sp->par.c = 0.25 + balance_rand(0.09);
270 sp->par.a = 10 + balance_rand(5);
271 sp->par.b = 0.35 + balance_rand(0.25);
273 sp->tumble.theta = 0;
275 sp->tumble.dtheta = 0;
289 sp->par.a = 0.2 + balance_rand(0.1);
290 sp->par.b = 27.0 + balance_rand(3.0);
292 sp->tumble.theta = 0;
294 sp->tumble.dtheta = -NRAND(2)*sp->par.c*sp->step;
300 sp->beecount = beemult * MI_COUNT(mi);
301 if (sp->beecount < 0) /* random variations */
302 sp->beecount = NRAND(-sp->beecount) + 1; /* Minimum 1 */
304 /* Clear the background. */
307 if(!allocated || sp->beecount != allocated){ /* reallocate */
308 if (sp->csegs != NULL) {
309 (void) free((void *) sp->csegs);
312 if (sp->cnsegs != NULL) {
313 (void) free((void *) sp->cnsegs);
316 if (sp->old_segs != NULL) {
317 (void) free((void *) sp->old_segs);
320 if (sp->p[0] != NULL) {
321 (void) free((void *) sp->p[0]);
324 if (sp->p[1] != NULL) {
325 (void) free((void *) sp->p[1]);
330 /* Allocate memory. */
333 sp->csegs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount
335 sp->cnsegs = (int *) malloc(sizeof (int) * MI_NPIXELS(mi));
337 sp->old_segs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount);
338 sp->p[0] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
339 sp->p[1] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
342 /* Initialize point positions, velocities, etc. */
344 for (b = 0; b < sp->beecount; b++) {
345 X(1, b) = X(0, b) = balance_rand(range.x);
346 Y(1, b) = Y(0, b) = balance_rand(range.y);
347 Z(1, b) = Z(0, b) = balance_rand(range.z);
352 draw_flow(ModeInfo * mi)
354 Display *display = MI_DISPLAY(mi);
355 Window window = MI_WINDOW(mi);
357 flowstruct *sp = &flows[MI_SCREEN(mi)];
360 double M[3][3]; /* transformation matrix */
362 if(!sp->view.depth){ /* simple 3D tumble */
363 double sint, cost, sinp, cosp;
364 sp->tumble.theta += sp->tumble.dtheta;
365 sp->tumble.phi += sp->tumble.dphi;
366 sint = sin(sp->tumble.theta);
367 cost = cos(sp->tumble.theta);
368 sinp = sin(sp->tumble.phi);
369 cosp = cos(sp->tumble.phi);
370 M[0][0]= cost; M[0][1]=-sint*cosp; M[0][2]= sint*sinp;
371 M[1][0]= sint; M[1][1]= cost*cosp; M[1][2]=-cost*sinp;
372 M[2][0]= 0; M[2][1]= 0; M[2][2]= 1;
373 } else { /* initialize matrix */
374 M[0][0]= 0; M[0][1]= 0; M[0][2]= 0;
375 M[1][0]= 0; M[1][1]= 0; M[1][2]= 0;
376 M[2][0]= 0; M[2][1]= 0; M[2][2]= 0;
380 for (col = 0; col < MI_NPIXELS(mi); col++)
383 MI_IS_DRAWN(mi) = True;
386 for (b = 0; b < sp->beecount; b++) {
387 /* Age the arrays. */
392 /* 2nd order Kunge Kutta */
396 k1 = sp->ODE(sp->par, X(1, b), Y(1, b), Z(1, b));
400 k2 = sp->ODE(sp->par, X(1, b) + k1.x, Y(1, b) + k1.y, Z(1, b) + k1.z);
404 X(0, b) = X(1, b) + (k1.x + k2.x) / 2.0;
405 Y(0, b) = Y(1, b) + (k1.y + k2.y) / 2.0;
406 Z(0, b) = Z(1, b) + (k1.z + k2.z) / 2.0;
409 /* Colour according to bee */
410 col = b % (MI_NPIXELS(mi) - 1);
411 ix = col * sp->beecount + sp->cnsegs[col];
413 /* Fill the segment lists. */
415 if(sp->view.depth) /* perspective view has special points */
416 if(b==0){ /* point of view */
417 sp->centre.x=X(0, b);
418 sp->centre.y=Y(0, b);
419 sp->centre.z=Z(0, b);
420 }else if(b==1){ /* neighbour: used to compute local axes */
421 double x[3], p[3], x2=0, xp=0;
425 x[0] = X(0, 0) - X(1, 0);
426 x[1] = Y(0, 0) - Y(1, 0);
427 x[2] = Z(0, 0) - Z(1, 0);
430 p[0] = X(0, 1) - X(1, 0);
431 p[1] = Y(0, 1) - Y(1, 0);
432 p[2] = Z(0, 1) - Z(1, 0);
435 x2+= x[i]*x[i]; /* X . X */
436 xp+= x[i]*p[i]; /* X . P */
437 M[0][i] = x[i]; /* X */
440 for(i=0; i<3; i++) /* (X x P) x X */
441 M[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
443 M[2][0] = x[1]*p[2] - x[2]*p[1]; /* X x P */
444 M[2][1] = -x[0]*p[2] + x[2]*p[0];
445 M[2][2] = x[0]*p[1] - x[1]*p[0];
450 for(i=0; i<3; i++) A+=M[j][i]*M[j][i]; /* sum squares */
452 for(i=0; i<3; i++) M[j][i]/=A;
455 X(0, 1)=X(0, 0)+M[1][0]; /* adjust neighbour */
456 Y(0, 1)=Y(0, 0)+M[1][1];
457 Z(0, 1)=Z(0, 0)+M[1][2];
459 #if 0 /* display local axes for testing */
464 X(0, b)=X(0, 0)+0.5*M[0][0];
465 Y(0, b)=Y(0, 0)+0.5*M[0][1];
466 Z(0, b)=Z(0, 0)+0.5*M[0][2];
471 X(0, b)=X(0, 0)+1.5*M[2][0];
472 Y(0, b)=Y(0, 0)+1.5*M[2][1];
473 Z(0, b)=Z(0, 0)+1.5*M[2][2];
481 double x=X(i,b)-sp->centre.x;
482 double y=Y(i,b)-sp->centre.y;
483 double z=Z(i,b)-sp->centre.z;
484 double X=M[0][0]*x + M[0][1]*y + M[0][2]*z;
485 double Y=M[1][0]*x + M[1][1]*y + M[1][2]*z;
486 double Z=M[2][0]*x + M[2][1]*y + M[2][2]*z+sp->view.height;
490 absx=SCALE_X(sp->view.depth*Y/X);
491 absy=SCALE_Y(sp->view.depth*Z/X);
492 if(absx < -sp->width || absx > 2*sp->width ||
493 absy < -sp->height || absy > 2*sp->height)
500 sp->csegs[ix].x1 = (short) absx;
501 sp->csegs[ix].y1 = (short) absy;
503 sp->csegs[ix].x2 = (short) absx;
504 sp->csegs[ix].y2 = (short) absy;
507 if(i == 2) /* both assigned */
510 if (sp->count) { /* erase */
511 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
512 XDrawSegments(display, window, gc, sp->old_segs, sp->nold_segs);
515 if (MI_NPIXELS(mi) > 2){ /* render colour */
516 for (col = 0; col < MI_NPIXELS(mi); col++)
517 if (sp->cnsegs[col] > 0) {
518 XSetForeground(display, gc, MI_PIXEL(mi, col));
519 XDrawSegments(display, window, gc,
520 sp->csegs + col * sp->beecount, sp->cnsegs[col]);
522 } else { /* render mono */
523 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
524 XDrawSegments(display, window, gc,
525 sp->csegs + col * sp->beecount, sp->cnsegs[col]);
528 /* Copy to erase-list */
529 for (col = 0, c = 0; col < MI_NPIXELS(mi); col++)
530 for (b = 0; b < sp->cnsegs[col]; b++)
531 sp->old_segs[c++] = (sp->csegs + col * sp->beecount)[b];
534 if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
539 release_flow(ModeInfo * mi)
544 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
545 flowstruct *sp = &flows[screen];
547 if (sp->csegs != NULL)
548 (void) free((void *) sp->csegs);
549 if (sp->cnsegs != NULL)
550 (void) free((void *) sp->cnsegs);
551 if (sp->old_segs != NULL)
552 (void) free((void *) sp->old_segs);
553 if (sp->p[0] != NULL)
554 (void) free((void *) sp->p[0]);
555 if (sp->p[1] != NULL)
556 (void) free((void *) sp->p[1]);
558 (void) free((void *) flows);
564 refresh_flow(ModeInfo * mi)