http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / flow.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* flow --- flow of strange bees */
3
4 #if 0
5 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Sun.COM>
10  * Portions added by Stephen Davies are Copyright (c) 2000 Stephen Davies
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 Poincare'
27  * sections of the "Birkhoff Bagel" and Duffing's forced occilator.
28  *
29  * Revision History:
30  * 21-Feb-00: Major hackage by Chalky (Stephen Davies, chalky@null.net)
31  *            Forced perspective mode, added 3d box around attractor which
32  *            involved coding 3d-planar-clipping against the view-frustrum
33  *            thingy. Also made view alternate between piggybacking on a 'bee'
34  *            to zooming around outside the attractor. Most bees slow down and
35  *            stop, to make the structure of the attractor more obvious.
36  * 31-Nov-98: [TDA] Added Duffing  (what a strange day that was :) DAB)
37  *   Duffing's forced oscillator has been added to the formula list and
38  *   the parameters section has been updated to display it in Poincare'
39  *   section.
40  * 30-Nov-98: [TDA] Added travelling perspective option
41  *   A more exciting point-of-view has been added to all autonomous flows.
42  *   This views the flow as seen by a particle moving with the flow.  In the
43  *   metaphor of the original code, I've attached a camera to one of the
44  *   trained bees!
45  * 30-Nov-98: [TDA] Much code cleanup.
46  * 09-Apr-97: [TDA] Ported to xlockmore-4
47  * 18-Jul-96: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
48  * 31-Aug-90: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
49  */
50
51 #ifdef STANDALONE
52 # define PROGCLASS      "Flow"
53 # define HACK_INIT      init_flow
54 # define HACK_DRAW      draw_flow
55 # define flow_opts      xlockmore_opts
56 # define DEFAULTS       "*delay:                1000 \n" \
57                                         "*count:                500 \n" \
58                                         "*cycles:               3000 \n" \
59                                         "*ncolors:              200 \n" \
60         "*rotate:         True \n" \
61         "*ride:           True \n" \
62         "*zoom:           True \n" \
63         "*allow2d:        True \n" \
64         "*box:            True \n" \
65         "*slow:           True \n" \
66         "*freeze:         True \n"
67 # define SMOOTH_COLORS
68 # include "xlockmore.h"         /* in xscreensaver distribution */
69 # include "erase.h"
70
71 #else /* STANDALONE */
72 # include "xlock.h"             /* in xlockmore distribution */
73 #endif /* STANDALONE */
74
75 XrmOptionDescRec flow_options[];
76 ModeSpecOpt flow_opts = { 7, flow_options, 0, NULL, NULL };
77
78 #ifdef USE_MODULES
79 ModStruct   flow_description = {
80         "flow", "init_flow", "draw_flow", "release_flow",
81         "refresh_flow", "init_flow", NULL, &flow_opts,
82         1000, 1024, 3000, 1, 64, 1.0, "",
83         "Shows dynamic strange attractors", 0, NULL
84 };
85
86 #endif
87
88 typedef struct {
89         double      x;
90         double      y;
91         double      z;
92 } dvector;
93
94 typedef struct {
95         double      a, b, c;
96 } Par;
97
98 /* Macros */
99 #define X(t,b)  (sp->p[t][b].x)
100 #define Y(t,b)  (sp->p[t][b].y)
101 #define Z(t,b)  (sp->p[t][b].z)
102 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
103 #define SCALE_X(A) (sp->width/2+sp->width/sp->size*(A))
104 /*#define SCALE_Y(A) (sp->height/2+sp->height/sp->size*(A))*/
105 #define SCALE_Y(A) (sp->height/2+sp->width/sp->size*(A))
106
107 /* Mode of operation. Rotate, ride and zoom are mutually exclusive */
108 typedef enum {
109         FLOW_ROTATE = 1, /* Rotate around attractor */
110         FLOW_RIDE = 2,   /* Ride a trained bee */
111         FLOW_ZOOM = 4,   /* Zoom in and out */
112         FLOW_2D = 8,     /* Allow 2D attractors */
113         FLOW_BOX = 16,    /* Compute a box around the attractor */
114         FLOW_SLOW = 32,   /* Some bees are slower (and have antifreeze) */
115         FLOW_FREEZE = 64  /* Freeze some of the bees in action */
116 } FlowMode;
117
118 #define FLOW_DEFAULT (FLOW_ROTATE|FLOW_RIDE|FLOW_ZOOM|FLOW_2D|\
119                 FLOW_BOX|FLOW_SLOW|FLOW_FREEZE)
120
121 typedef struct {
122         int         width;
123         int         height;
124         int         count;
125         double      size;
126         
127         int         beecount;   /* number of bees */
128         XSegment   *csegs;          /* bee lines */
129         int        *cnsegs;
130         XSegment   *old_segs;   /* old bee lines */
131         int         nold_segs;
132         double      step;
133         double          slow;
134         double          slow_view;
135         dvector     centre;             /* centre */
136         dvector         range;
137         struct {
138                 double  depth;
139                 double  height;
140         }           view;
141         dvector         circle[2]; /* POV that circles around the scene */
142         dvector    *p[2];   /* bee positions x[time][bee#] */
143         struct {
144                 double  theta;
145                 double  dtheta;
146                 double  phi;
147                 double  dphi;
148         }           tumble;
149         dvector  (*ODE) (Par par, double x, double y, double z);
150         Par         par;
151         FlowMode                mode; /* Mode of operation */
152 } flowstruct;
153
154 static flowstruct *flows = NULL;
155
156 static dvector
157 Lorentz(Par par, double x, double y, double z)
158 {
159         dvector d;
160
161         d.x = par.a * (y - x);
162         d.y = x * (par.b - z) - y;
163         d.z = x * y - par.c * z;
164         return d;
165 }
166
167 static dvector
168 Rossler(Par par, double x, double y, double z)
169 {
170         dvector d;
171
172         d.x = -(y + par.a * z);
173         d.y = x + y * par.b;
174         d.z = par.c + z * (x - 5.7);
175         return d;
176 }
177
178 static dvector
179 RosslerCone(Par par, double x, double y, double z)
180 {
181         dvector d;
182
183         d.x = -(y + par.a * z);
184         d.y = x + y * par.b - z * z * par.c;
185         d.z = 0.2 + z * (x - 5.7);
186         return d;
187 }
188
189 static dvector
190 Birkhoff(Par par, double x, double y, double z)
191 {
192         dvector d;
193
194         d.x = -y + par.b * sin(z);
195         d.y = 0.7 * x + par.a * y * (0.1 - x * x);
196         d.z = par.c;
197         return d;
198 }
199
200 static dvector
201 Duffing(Par par, double x, double y, double z)
202 {
203         dvector d;
204
205         d.x = -par.a * x - y/2 - y * y * y/8 + par.b * cos(z);
206         d.y = 2*x;
207         d.z = par.c;
208         return d;
209 }
210
211 void init_clip(flowstruct *sp);
212
213 void
214 init_flow(ModeInfo * mi)
215 {
216         flowstruct *sp;
217         int         b;
218         double      beemult = 1;
219         static int  allocated = 0;
220
221         if (flows == NULL) {
222                 if ((flows = (flowstruct *) calloc(MI_NUM_SCREENS(mi),
223                                                sizeof (flowstruct))) == NULL)
224                         return;
225         }
226         sp = &flows[MI_SCREEN(mi)];
227
228         sp->count = 0;
229         sp->slow = 0.999;
230         sp->slow_view = 0.90;
231
232         sp->width = MI_WIDTH(mi);
233         sp->height = MI_HEIGHT(mi);
234
235         sp->tumble.theta = balance_rand(M_PI);
236         sp->tumble.phi = balance_rand(M_PI);
237         sp->tumble.dtheta = 0.002;
238         sp->tumble.dphi = 0.001;
239         sp->view.height = 0;
240         sp->view.depth = 0; /* no perspective view */
241         sp->mode = 0;
242    if (get_boolean_resource ("rotate", "Boolean")) sp->mode |= FLOW_ROTATE;
243    if (get_boolean_resource ("ride", "Boolean")) sp->mode |= FLOW_RIDE;
244    if (get_boolean_resource ("zoom", "Boolean")) sp->mode |= FLOW_ZOOM;
245    if (get_boolean_resource ("allow2d", "Boolean")) sp->mode |= FLOW_2D;
246    if (get_boolean_resource ("slow", "Boolean")) sp->mode |= FLOW_SLOW;
247    if (get_boolean_resource ("freeze", "Boolean")) sp->mode |= FLOW_FREEZE;
248    if (get_boolean_resource ("box", "Boolean")) sp->mode |= FLOW_BOX;
249
250         b = (sp->mode & FLOW_2D) ? 5 : 3;
251         b = NRAND(b);
252
253         /* If more than one of rotate, ride and zoom are set, choose one */
254         if (b < 3) {
255                 int num = 0, modes[3];
256
257                 if (sp->mode & FLOW_ROTATE) modes[num++] = FLOW_ROTATE;
258                 if (sp->mode & FLOW_RIDE) modes[num++] = FLOW_RIDE;
259                 if (sp->mode & FLOW_ZOOM) modes[num++] = FLOW_ZOOM;
260
261                 sp->mode &= ~(FLOW_ROTATE | FLOW_RIDE | FLOW_ZOOM);
262
263                 if (num) sp->mode |= modes[ NRAND(num) ];
264                 else sp->mode |= FLOW_ZOOM;
265         }
266         
267         switch (b) {
268         case 0:
269                 sp->view.depth = 10;
270                 sp->view.height = 0.2;
271                 beemult = 3;
272                 sp->ODE = Lorentz;
273                 sp->step = 0.02;
274                 sp->size = 60;
275                 sp->centre.x = 0;
276                 sp->centre.y = 0;
277                 sp->centre.z = 24;
278                 sp->range.x = 5;
279                 sp->range.y = 5;
280                 sp->range.z = 1;
281                 sp->par.a = 10 + balance_rand(5);
282                 sp->par.b = 28 + balance_rand(5);
283                 sp->par.c = 2 + balance_rand(1);
284                 break;
285         case 1:
286                 sp->view.depth = 10;
287                 sp->view.height = 0.1;
288                 beemult = 4;
289                 sp->ODE = Rossler;
290                 sp->step = 0.05;
291                 sp->size = 24;
292                 sp->centre.x = 0;
293                 sp->centre.y = 0;
294                 sp->centre.z = 3;
295                 sp->range.x = 4;
296                 sp->range.y = 4;
297                 sp->range.z = 7;
298                 sp->par.a = 2 + balance_rand(1);
299                 sp->par.b = 0.2 + balance_rand(0.1);
300                 sp->par.c = 0.2 + balance_rand(0.1);
301                 break;
302         case 2:
303                 sp->view.depth = 10;
304                 sp->view.height = 0.1;
305                 beemult = 3;
306                 sp->ODE = RosslerCone;
307                 sp->step = 0.05;
308                 sp->size = 24;
309                 sp->centre.x = 0;
310                 sp->centre.y = 0;
311                 sp->centre.z = 3;
312                 sp->range.x = 4;
313                 sp->range.y = 4;
314                 sp->range.z = 4;
315                 sp->par.a = 2;
316                 sp->par.b = 0.2;
317                 sp->par.c = 0.25 + balance_rand(0.09);
318                 break;
319         case 3:
320                 sp->ODE = Birkhoff;
321                 sp->step = 0.04;
322                 sp->size = 2.6;
323                 sp->centre.x = 0;
324                 sp->centre.y = 0;
325                 sp->centre.z = 0;
326                 sp->range.x = 3;
327                 sp->range.y = 4;
328                 sp->range.z = 0;
329                 sp->par.a = 10 + balance_rand(5);
330                 sp->par.b = 0.35 + balance_rand(0.25);
331                 sp->par.c = 1.57;
332                 sp->tumble.theta = 0;
333                 sp->tumble.phi = 0;
334                 sp->tumble.dtheta = 0;
335                 sp->tumble.dphi = 0;
336                 break;
337         case 4:
338         default:
339                 sp->ODE = Duffing;
340                 sp->step = 0.02;
341                 sp->size = 30;
342                 sp->centre.x = 0;
343                 sp->centre.y = 0;
344                 sp->centre.z = 0;
345                 sp->range.x = 20;
346                 sp->range.y = 20;
347                 sp->range.z = 0;
348                 sp->par.a = 0.2 + balance_rand(0.1);
349                 sp->par.b = 27.0 + balance_rand(3.0);
350                 sp->par.c = 1.33;
351                 sp->tumble.theta = 0;
352                 sp->tumble.phi = 0;
353                 sp->tumble.dtheta = -NRAND(2)*sp->par.c*sp->step;
354                 sp->tumble.dphi = 0;
355                 beemult = 0.5;
356                 break;
357         }
358
359         sp->view.depth *= 4;
360
361         sp->beecount = beemult * MI_COUNT(mi);
362         if (sp->beecount < 0)   /* random variations */ 
363                 sp->beecount = NRAND(-sp->beecount) + 1; /* Minimum 1 */
364
365         /* Clear the background. */
366         MI_CLEARWINDOW(mi);
367
368         if(!allocated || sp->beecount != allocated){ /* reallocate */
369                 if (sp->csegs != NULL) {
370                         (void) free((void *) sp->csegs);
371                         sp->csegs = NULL;
372                 }
373                 if (sp->cnsegs != NULL) {
374                         (void) free((void *) sp->cnsegs);
375                         sp->cnsegs = NULL;
376                 }
377                 if (sp->old_segs != NULL) {
378                         (void) free((void *) sp->old_segs);
379                         sp->old_segs = NULL;
380                 }
381                 if (sp->p[0] != NULL) {
382                         (void) free((void *) sp->p[0]);
383                         sp->p[0] = NULL;
384                 }
385                 if (sp->p[1] != NULL) {
386                         (void) free((void *) sp->p[1]);
387                         sp->p[1] = NULL;
388                 }
389                 allocated = sp->beecount;
390         }
391
392         /* Allocate memory. */
393
394         if (!sp->csegs) {
395                 sp->csegs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount
396                                                 * MI_NPIXELS(mi));
397                 sp->cnsegs = (int *) malloc(sizeof (int) * MI_NPIXELS(mi));
398
399                 sp->old_segs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount);
400                 sp->p[0] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
401                 sp->p[1] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
402         }
403
404         /* Initialize point positions, velocities, etc. */
405
406         for (b = 0; b < sp->beecount; b++) {
407                 X(1, b) = X(0, b) = balance_rand(sp->range.x);
408                 Y(1, b) = Y(0, b) = balance_rand(sp->range.y);
409                 Z(1, b) = Z(0, b) = balance_rand(sp->range.z);
410         }
411
412         init_clip(sp);
413
414 }
415
416 /* Clipping planes */
417 #define PLANES 5
418 static double plane_orig[][2][3] = {
419         /* X goes into screen, Y goes right, Z goes down(up?) */
420         /* {Normal}, {Point} */
421         { {1.0, 0, 0}, {0.01, 0, 0} },
422         { {1.0, 1.0, 0.0}, {0, 0, 0} },
423         { {1.0,-1.0, 0.0}, {0, 0, 0} },
424         { {1.0, 0.0, 1.0}, {0, 0, 0} },
425         { {1.0, 0.0,-1.0}, {0, 0, 0} }
426 };
427 static double plane[PLANES][2][3];
428 static double plane_d[PLANES];
429
430 #define BOX_P 32
431 #define BOX_L 36
432 #define MIN_BOX (3)
433 #define MAX_BOX (MIN_BOX + BOX_L)
434 /* Points that make up the box (normalized coordinates) */
435 static double box_orig[][3] = {
436         {1,1,1},   /* 0 */
437         {1,1,-1},  /* 1 */
438         {1,-1,-1}, /* 2 */
439         {1,-1,1},  /* 3 */
440         {-1,1,1},  /* 4 */
441         {-1,1,-1}, /* 5 */
442         {-1,-1,-1},/* 6 */
443         {-1,-1,1}, /* 7 */
444         {1, .8, .8},
445         {1, .8,-.8},
446         {1,-.8,-.8},
447         {1,-.8, .8},
448         { .8,1, .8},
449         { .8,1,-.8},
450         {-.8,1,-.8},
451         {-.8,1, .8},
452         { .8, .8,1},
453         { .8,-.8,1},
454         {-.8,-.8,1},
455         {-.8, .8,1},
456         {-1, .8, .8},
457         {-1, .8,-.8},
458         {-1,-.8,-.8},
459         {-1,-.8, .8},
460         { .8,-1, .8},
461         { .8,-1,-.8},
462         {-.8,-1,-.8},
463         {-.8,-1, .8},
464         { .8, .8,-1},
465         { .8,-.8,-1},
466         {-.8,-.8,-1},
467         {-.8, .8,-1}
468 };
469
470 /* Container for scaled box points */
471 static double box[BOX_P][3];
472
473 /* Lines connecting the box dots */
474 static double lines[][2] = {
475         {0,1}, {1,2}, {2,3}, {3,0}, /* box */
476         {4,5}, {5,6}, {6,7}, {7,4},
477         {0,4}, {1,5}, {2,6}, {3,7},
478         {4+4,5+4}, {5+4,6+4}, {6+4,7+4}, {7+4,4+4},
479         {4+8,5+8}, {5+8,6+8}, {6+8,7+8}, {7+8,4+8},
480         {4+12,5+12}, {5+12,6+12}, {6+12,7+12}, {7+12,4+12},
481         {4+16,5+16}, {5+16,6+16}, {6+16,7+16}, {7+16,4+16},
482         {4+20,5+20}, {5+20,6+20}, {6+20,7+20}, {7+20,4+20},
483         {4+24,5+24}, {5+24,6+24}, {6+24,7+24}, {7+24,4+24},
484 };
485         
486 /* Boundaries of bees */
487 double xmin, xmax;
488 double ymin, ymax;
489 double zmin, zmax;
490
491 void init_clip(flowstruct *sp)
492 {
493         int i;
494
495         /* Scale the planes to the screen. I had to invert the projection
496          * algorithms so that when projected they would be right at the edge of the
497          * screen. */
498
499     /* #### jwz: I'm not really sure what it means when sp->view.depth is 0
500        in here -- what's the right thing to do? */
501
502         double width = (sp->view.depth
503                     ? sp->size/sp->view.depth/2
504                     : 1);
505         double height = (sp->view.depth
506                      ? (sp->size/sp->view.depth/2*
507                         sp->view.height/sp->view.height)
508                      : 1);
509         for (i = 0; i < PLANES; i++) {
510                 /* Copy orig planes into planes, expanding <-> clippings */
511                 plane[i][0][0] = plane_orig[i][0][0];
512                 plane[i][0][1] = plane_orig[i][0][1] / width;
513                 plane[i][0][2] = plane_orig[i][0][2] / height;
514                 plane[i][1][0] = plane_orig[i][1][0];
515                 plane[i][1][1] = plane_orig[i][1][1];
516                 plane[i][1][2] = plane_orig[i][1][2];
517                 
518                 /* Calculate the 'd' part of 'ax + by + cz = d' */
519                 plane_d[i] = - plane[i][0][0] * plane[i][1][0];
520                 plane_d[i] -= plane[i][0][1] * plane[i][1][1];
521                 plane_d[i] -= plane[i][0][2] * plane[i][1][2];
522         }
523         xmin = X(0, i); xmax = X(0,i);
524         ymin = Y(0, i); ymax = Y(0,i);
525         zmin = Z(0, i); zmax = Z(0,i);
526 }
527
528 /* Scale the box defined above to fit around all points */
529 void create_box(flowstruct *sp)
530 {
531         int i = MAX_BOX;
532         double xmid, ymid, zmid;
533         double xsize, ysize, zsize;
534         double size;
535
536         /* Count every 5th point for speed.. */
537         for (; i < sp->beecount; i += 5) {
538                 if ( X(0,i) < xmin ) xmin = X(0, i);
539                 else if ( X(0,i) > xmax ) xmax = X(0, i);
540                 if ( Y(0,i) < ymin ) ymin = Y(0, i);
541                 else if ( Y(0,i) > ymax ) ymax = Y(0, i);
542                 if ( Z(0,i) < zmin ) zmin = Z(0, i);
543                 else if ( Z(0,i) > zmax ) zmax = Z(0, i);
544         }
545         xmid = (xmax+xmin)/2;
546         ymid = (ymax+ymin)/2;
547         zmid = (zmax+zmin)/2;
548         xsize = xmax - xmin;
549         ysize = ymax - ymin;
550         zsize = zmax - zmin;
551         size = xsize;
552         if (ysize> size) size = ysize;
553         if (zsize > size) size = zsize;
554         size /= 2;
555
556         /* Scale box */
557         for (i = 0; i < BOX_P; i++) {
558                 box[i][0] = box_orig[i][0] * size + xmid;
559                 box[i][1] = box_orig[i][1] * size + ymid;
560                 box[i][2] = box_orig[i][2] * size + zmid;
561         }
562
563 }
564
565 /* Returns true if point is infront of the plane (rather than behind) */
566 int infront_of(double x, double y, double z, int i)
567 {
568         double sum = plane[i][0][0]*x + plane[i][0][1]*y + plane[i][0][2]*z + plane_d[i];
569         return sum >= 0.0;
570 }
571
572 /* Returns true if line was behind a clip plane, or clips the line */
573 int clip(double *x1, double *y1, double *z1, double *x2, double *y2, double *z2)
574 {
575         int i;
576         for (i = 0; i < PLANES; i++) {
577                 double t;
578                 double x, y, z; /* Intersection point */
579                 double dx, dy, dz; /* line delta */
580                 int front1, front2;
581                 front1 = infront_of(*x1, *y1, *z1, i);
582                 front2 = infront_of(*x2, *y2, *z2, i);
583                 if (!front1 && !front2) return 1;
584                 if (front1 && front2) continue;
585
586                 dx = *x2 - *x1;
587                 dy = *y2 - *y1;
588                 dz = *z2 - *z1;
589
590                 /* Find t in line equation */
591                 t = ( plane_d[i] - 
592                                 plane[i][0][0]*(*x1) - plane[i][0][1]*(*y1) - plane[i][0][2]*(*z1) ) 
593                                 / 
594                         ( plane[i][0][0]*dx + plane[i][0][1]*dy + plane[i][0][2]*dz );
595
596                 x = *x1 + dx * t;
597                 y = *y1 + dy * t;
598                 z = *z1 + dz * t;
599                 /* Make point that was behind to be the intersect */
600                 if (front2) {
601                         *x1 = x;
602                         *y1 = y;
603                         *z1 = z;
604                 } else {
605                         *x2 = x;
606                         *y2 = y;
607                         *z2 = z;
608                 }
609         }
610         return 0;
611 }       
612
613
614 void
615 draw_flow(ModeInfo * mi)
616 {
617         Display    *display = MI_DISPLAY(mi);
618         Window      window = MI_WINDOW(mi);
619         GC          gc = MI_GC(mi);
620         flowstruct *sp = &flows[MI_SCREEN(mi)];
621         int         b, c, i;
622         int         col, ix;
623         int                     new_view = 0;
624         double      M[3][3]; /* transformation matrix */
625         double          step_view = sp->step;
626         double          step_bees = sp->step;
627         double          step_slow = sp->step;
628         double          pp, pc;
629
630         create_box(sp);
631
632         if(!sp->view.depth){ /* simple 3D tumble */
633                 double      sint, cost, sinp, cosp;
634                 sp->tumble.theta += sp->tumble.dtheta;
635                 sp->tumble.phi += sp->tumble.dphi;
636                 sint = sin(sp->tumble.theta);
637                 cost = cos(sp->tumble.theta);
638                 sinp = sin(sp->tumble.phi);
639                 cosp = cos(sp->tumble.phi);
640                 M[0][0]= cost; M[0][1]=-sint*cosp; M[0][2]= sint*sinp;
641                 M[1][0]= sint; M[1][1]= cost*cosp; M[1][2]=-cost*sinp;
642                 M[2][0]= 0;    M[2][1]= 0;         M[2][2]= 1;
643         } else { /* initialize matrix */
644                 M[0][0]= 0; M[0][1]= 0; M[0][2]= 0;
645                 M[1][0]= 0; M[1][1]= 0; M[1][2]= 0;
646                 M[2][0]= 0; M[2][1]= 0; M[2][2]= 0;
647
648         }
649
650         for (col = 0; col < MI_NPIXELS(mi); col++)
651                 sp->cnsegs[col] = 0;
652
653         MI_IS_DRAWN(mi) = True;
654
655         /* Calculate circling POV */
656         sp->circle[1] = sp->circle[0];
657         sp->circle[0].x = sp->size * 2 * sin(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
658         sp->circle[0].y = sp->size * 2 * cos(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
659         sp->circle[0].z = sp->size * 2 * sin(sp->count / 421.0);
660
661         if (sp->mode & FLOW_ROTATE)
662                 pp = 0;
663         else if (sp->mode & FLOW_RIDE)
664                 pp = 1;
665         else /* ZOOM */
666                 /* Bistable oscillator to switch between the trained bee and the circler */
667                 pp = -sin(sin(sin(cos(sp->count / 150.0)*M_PI/2)*M_PI/2)*M_PI/2) *0.5 + 0.5;
668         pc = 1 - pp;
669
670
671         /* Slow down or speed up the bees / view: */
672
673         /* exponentially accelerate towards zero */
674         sp->slow = sp->slow * 1.005 - 0.005; 
675         if (sp->slow < 0) sp->slow = 0;
676
677         sp->slow_view = sp->slow_view * 1.005 - 0.005;
678         if (sp->slow_view < 0) sp->slow_view = 0;
679
680         /* View speeds up, slow bees slow to half speed, and other bees will
681          * actually stop */
682         step_view = step_view * (1.01 - sp->slow_view * sp->slow_view) * 0.2;
683         step_slow = step_slow * (sp->slow + 0.5) / 2;
684         if (sp->mode & FLOW_SLOW)
685                 step_bees = step_bees * sp->slow;
686         else
687                 step_bees = step_slow;
688
689         /* <=- Bees -=> */
690         for (b = 0; b < sp->beecount; b++) {
691                 /* Calc if this bee is slow. Note normal bees are exempt from
692                  * calculations once they slow to half speed, so that they remain as
693                  * frozen lines rather than barely-visible points */
694                 int slow = ((b & 0x7) == 0);
695                 if ( !(sp->mode & FLOW_FREEZE) ) slow = 1;
696                 /* Age the arrays. */
697                 if (b < 2 || sp->slow > 0.5 || slow) {
698                         X(1, b) = X(0, b);
699                         Y(1, b) = Y(0, b);
700                         Z(1, b) = Z(0, b);
701
702                         /* 2nd order Kunge Kutta */
703                         {
704                                 dvector     k1, k2;
705                                 double          step;
706
707                                 if (b == 0 || b == 1) {
708                                         step = step_view;
709                                 } else if (slow) {
710                                         step = step_slow;
711                                 } else {
712                                         step = step_bees;
713                                 }
714                                 k1 = sp->ODE(sp->par, X(1, b), Y(1, b), Z(1, b));
715                                 k1.x *= step;
716                                 k1.y *= step;
717                                 k1.z *= step;
718                                 k2 = sp->ODE(sp->par, X(1, b) + k1.x, Y(1, b) + k1.y, Z(1, b) + k1.z);
719                                 k2.x *= step;
720                                 k2.y *= step;
721                                 k2.z *= step;
722                                 X(0, b) = X(1, b) + (k1.x + k2.x) / 2.0;
723                                 Y(0, b) = Y(1, b) + (k1.y + k2.y) / 2.0;
724                                 Z(0, b) = Z(1, b) + (k1.z + k2.z) / 2.0;
725                         }
726                 }
727
728
729                 /* Colour according to bee */
730                 col = b % (MI_NPIXELS(mi) - 1);
731                 ix = col * sp->beecount + sp->cnsegs[col];
732
733                 /* Fill the segment lists. */
734
735                 if(sp->view.depth) { /* perspective view has special points */
736                         if(b==0){ /* point of view */
737                                 sp->centre.x = X(0, b) * pp + sp->circle[0].x * pc;
738                                 sp->centre.y = Y(0, b) * pp + sp->circle[0].y * pc;
739                                 sp->centre.z = Z(0, b) * pp + sp->circle[0].z * pc;
740                                 /*printf("center: (%3.3f,%3.3f,%3.3f)\n",sp->centre.x, sp->centre.y, sp->centre.z);*/
741                         }else if(b==1){ /* neighbour: used to compute local axes */
742                                 double x[3], p[3], x2=0, xp=0, C[3][3];
743                                 int j;
744
745                                 /* forward */                           
746                                 x[0] = X(0, 0) - X(1, 0);
747                                 x[1] = Y(0, 0) - Y(1, 0);
748                                 x[2] = Z(0, 0) - Z(1, 0);
749                         
750                                 /* neighbour */
751                                 p[0] = X(0, 1) - X(1, 0);
752                                 p[1] = Y(0, 1) - Y(1, 0);
753                                 p[2] = Z(0, 1) - Z(1, 0);
754
755                                 for(i=0; i<3; i++){
756                                         x2+= x[i]*x[i];    /* X . X */
757                                         xp+= x[i]*p[i];    /* X . P */
758                                         M[0][i] = x[i];    /* X */
759                                 }
760
761                                 for(i=0; i<3; i++)               /* (X x P) x X */
762                                         M[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
763                                 
764                                 M[2][0] =  x[1]*p[2] - x[2]*p[1]; /* X x P */
765                                 M[2][1] = -x[0]*p[2] + x[2]*p[0];
766                                 M[2][2] =  x[0]*p[1] - x[1]*p[0];
767
768                                 /* normalise axes */
769                                 for(j=0; j<3; j++){
770                                         double A=0;
771                                         for(i=0; i<3; i++) A+=M[j][i]*M[j][i]; /* sum squares */
772                                         A=sqrt(A);
773                                         for(i=0; i<3; i++) M[j][i]/=A;
774                                 }
775
776                                 X(0, 1)=X(0, 0)+M[1][0]; /* adjust neighbour */
777                                 Y(0, 1)=Y(0, 0)+M[1][1];
778                                 Z(0, 1)=Z(0, 0)+M[1][2];
779
780                                 /* Look at trained bee into C matrix */
781                                 /* forward */                           
782                                 x[0] = 0 - sp->circle[0].x;
783                                 x[1] = 0 - sp->circle[0].y;
784                                 x[2] = 0 - sp->circle[0].z;
785                         
786                                 /* neighbour */
787                                 p[0] = sp->circle[0].x - sp->circle[1].x;
788                                 p[1] = sp->circle[0].y - sp->circle[1].y;
789                                 p[2] = sp->circle[0].z - sp->circle[1].z;
790
791                                 for(i=0; i<3; i++){
792                                         x2+= x[i]*x[i];    /* X . X */
793                                         xp+= x[i]*p[i];    /* X . P */
794                                         C[0][i] = x[i];    /* X */
795                                 }
796
797                                 for(i=0; i<3; i++)               /* (X x P) x X */
798                                         C[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
799                                 
800                                 C[2][0] =  x[1]*p[2] - x[2]*p[1]; /* X x P */
801                                 C[2][1] = -x[0]*p[2] + x[2]*p[0];
802                                 C[2][2] =  x[0]*p[1] - x[1]*p[0];
803
804                                 /* normalise axes */
805                                 for(j=0; j<3; j++){
806                                         double A=0;
807                                         for(i=0; i<3; i++) A+=C[j][i]*C[j][i]; /* sum squares */
808                                         A=sqrt(A);
809                     if (A != 0) /* #### is this right? */
810                       for(i=0; i<3; i++) C[j][i]/=A;
811                                 }
812
813                                 /* Interpolate between Center and Trained Bee matrices */
814                                 /* This isn't very accurate and leads to weird transformations
815                                  * (shearing, etc), but it works. Besides, sometimes they look
816                                  * cool :) */
817                                 pp = pp * pp; /* Don't follow bee's direction until very close */
818                                 pc = 1 - pp;
819                                 for (i = 0; i < 3; i++)
820                                         for (j = 0; j < 3; j++)
821                                                 M[i][j] = M[i][j] * pp + C[i][j] * pc;
822                                 
823
824 #if 0  /* display local axes for testing */
825                                 X(1, b)=X(0, 0);
826                                 Y(1, b)=Y(0, 0);
827                                 Z(1, b)=Z(0, 0);
828                         }else if(b==2){
829                                 X(0, b)=X(0, 0)+0.5*M[0][0];
830                                 Y(0, b)=Y(0, 0)+0.5*M[0][1];
831                                 Z(0, b)=Z(0, 0)+0.5*M[0][2];
832                                 X(1, b)=X(0, 0);
833                                 Y(1, b)=Y(0, 0);
834                                 Z(1, b)=Z(0, 0);
835                         }else if(b==3){
836                                 X(0, b)=X(0, 0)+1.5*M[2][0];
837                                 Y(0, b)=Y(0, 0)+1.5*M[2][1];
838                                 Z(0, b)=Z(0, 0)+1.5*M[2][2];
839                                 X(1, b)=X(0, 0);
840                                 Y(1, b)=Y(0, 0);
841                                 Z(1, b)=Z(0, 0);
842 #endif
843                         /* Draw a box... */
844                         }
845                 }
846                         if (b >= MIN_BOX && b < MAX_BOX) {
847                                 int p1 = lines[b-MIN_BOX][0];
848                                 int p2 = lines[b-MIN_BOX][1];
849                                 X(0, b) = box[p1][0]; Y(0, b) = box[p1][1]; Z(0, b) = box[p1][2];
850                                 X(1, b) = box[p2][0]; Y(1, b) = box[p2][1]; Z(1, b) = box[p2][2];
851                         }
852                 
853                 
854 #if 0   /* Original code */
855                 for(i=0; i<2; i++){
856                         double x=X(i,b)-sp->centre.x;
857                         double y=Y(i,b)-sp->centre.y;
858                         double z=Z(i,b)-sp->centre.z;
859                         double X=M[0][0]*x + M[0][1]*y + M[0][2]*z;
860                         double Y=M[1][0]*x + M[1][1]*y + M[1][2]*z;
861                         double Z=M[2][0]*x + M[2][1]*y + M[2][2]*z+sp->view.height;
862                         double absx, absy;                              
863                         if(sp->view.depth){
864                                 if(X <= 0) break;
865                                 absx=SCALE_X(sp->view.depth*Y/X);
866                                 absy=SCALE_Y(sp->view.depth*Z/X);
867                                 if(absx < -sp->width || absx > 2*sp->width ||
868                                    absy < -sp->height || absy > 2*sp->height)
869                                         break;
870                         }else{
871                                 absx=SCALE_X(X);
872                                 absy=SCALE_Y(Y);
873                         }
874                         if(i){
875                                 sp->csegs[ix].x1 = (short) absx;
876                                 sp->csegs[ix].y1 = (short) absy;
877                         }else{
878                                 sp->csegs[ix].x2 = (short) absx;
879                                 sp->csegs[ix].y2 = (short) absy;
880                         }
881                 }
882                 if(i == 2) /* both assigned */
883                         sp->cnsegs[col]++;
884 #else   
885                 /* Chalky's code w/ clipping */
886                 if (b < ((sp->mode & FLOW_BOX) ? 2 : MAX_BOX))
887                         continue;
888                 do {
889                         double x1=X(0,b)-sp->centre.x;
890                         double y1=Y(0,b)-sp->centre.y;
891                         double z1=Z(0,b)-sp->centre.z;
892                         double X1=M[0][0]*x1 + M[0][1]*y1 + M[0][2]*z1;
893                         double Y1=M[1][0]*x1 + M[1][1]*y1 + M[1][2]*z1;
894                         double Z1=M[2][0]*x1 + M[2][1]*y1 + M[2][2]*z1+sp->view.height;
895                         double absx1, absy1;                            
896                         double x2=X(1,b)-sp->centre.x;
897                         double y2=Y(1,b)-sp->centre.y;
898                         double z2=Z(1,b)-sp->centre.z;
899                         double X2=M[0][0]*x2 + M[0][1]*y2 + M[0][2]*z2;
900                         double Y2=M[1][0]*x2 + M[1][1]*y2 + M[1][2]*z2;
901                         double Z2=M[2][0]*x2 + M[2][1]*y2 + M[2][2]*z2+sp->view.height;
902                         double absx2, absy2;                            
903                         if(sp->view.depth){
904                                 /* Need clipping if: is part of box, or close to viewer */
905                                 if ( (b >= MIN_BOX && b < MAX_BOX) || X1 <= 0.1 || X2 < 0.1) 
906                                         if (clip(&X1, &Y1, &Z1, &X2, &Y2, &Z2))
907                                                 break;
908                                 if (X1 <= 0 || X2 <= 0) break;
909                                 absx1=SCALE_X(sp->view.depth*Y1/X1);
910                                 absy1=SCALE_Y(sp->view.depth*Z1/X1);
911                                 if(absx1 < -sp->width || absx1 > 2*sp->width ||
912                                    absy1 < -sp->height || absy1 > 2*sp->height)
913                                         break;
914                                 absx2=SCALE_X(sp->view.depth*Y2/X2);
915                                 absy2=SCALE_Y(sp->view.depth*Z2/X2);
916                                 if(absx2 < -sp->width || absx2 > 2*sp->width ||
917                                    absy2 < -sp->height || absy2 > 2*sp->height)
918                                         break;
919                         }else{
920                                 absx1=SCALE_X(X1);
921                                 absy1=SCALE_Y(Y1);
922                                 absx2=SCALE_X(X2);
923                                 absy2=SCALE_Y(Y2);
924                         }
925
926                         sp->csegs[ix].x1 = (short) absx1;
927                         sp->csegs[ix].y1 = (short) absy1;
928                         sp->csegs[ix].x2 = (short) absx2;
929                         sp->csegs[ix].y2 = (short) absy2;
930
931                         sp->cnsegs[col]++;
932                 } while (0);
933 #endif
934         }
935
936         if (sp->count) { /* erase */
937                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
938                 XDrawSegments(display, window, gc, sp->old_segs, sp->nold_segs);
939         }
940
941         if (MI_NPIXELS(mi) > 2){ /* render colour */
942                 for (col = 0; col < MI_NPIXELS(mi); col++)
943                         if (sp->cnsegs[col] > 0) {
944                                 XSetForeground(display, gc, MI_PIXEL(mi, col));
945                                 XDrawSegments(display, window, gc,
946                                               sp->csegs + col * sp->beecount, sp->cnsegs[col]);
947                         }
948         } else {                /* render mono */
949                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
950                 XDrawSegments(display, window, gc,
951                                           sp->csegs + col * sp->beecount, sp->cnsegs[col]);
952         }
953
954         /* Copy to erase-list */
955         for (col = 0, c = 0; col < MI_NPIXELS(mi); col++)
956                 for (b = 0; b < sp->cnsegs[col]; b++)
957                         sp->old_segs[c++] = (sp->csegs + col * sp->beecount)[b];
958         sp->nold_segs = c;
959
960         if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
961                 init_flow(mi);
962
963         if (sp->count % (MI_CYCLES(mi)/4) == 0) { /* pick a new view */
964                 new_view = 0; /* change to 1 .. */
965         }
966
967         if (X(0, 0) < xmin*2 || X(0, 0) > xmax*2) new_view = 1;
968         if (Y(0, 0) < ymin*2 || Y(0, 0) > ymax*2) new_view = 1;
969         if (Z(0, 0) < zmin*2 || Z(0, 0) > zmax*2) new_view = 1;
970
971         if (new_view) {
972                 for (b = 0; b < 2; b++) {
973                         X(1, b) = X(0, b) = balance_rand(sp->range.x*4);
974                         Y(1, b) = Y(0, b) = balance_rand(sp->range.y*4);
975                         Z(1, b) = Z(0, b) = balance_rand(sp->range.z*4);
976                 }
977                 sp->slow_view = 0.90;
978         }
979 }
980
981 void
982 release_flow(ModeInfo * mi)
983 {
984         if (flows != NULL) {
985                 int         screen;
986
987                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
988                         flowstruct *sp = &flows[screen];
989
990                         if (sp->csegs != NULL)
991                                 (void) free((void *) sp->csegs);
992                         if (sp->cnsegs != NULL)
993                                 (void) free((void *) sp->cnsegs);
994                         if (sp->old_segs != NULL)
995                                 (void) free((void *) sp->old_segs);
996                         if (sp->p[0] != NULL)
997                                 (void) free((void *) sp->p[0]);
998                         if (sp->p[1] != NULL)
999                                 (void) free((void *) sp->p[1]);
1000                 }
1001                 (void) free((void *) flows);
1002                 flows = NULL;
1003         }
1004 }
1005
1006 void
1007 refresh_flow(ModeInfo * mi)
1008 {
1009         MI_CLEARWINDOW(mi);
1010 }
1011
1012 XrmOptionDescRec flow_options[] =
1013 {
1014         {"-rotate",  ".rotate", XrmoptionSepArg, 0},
1015         {"-ride",  ".ride", XrmoptionSepArg, 0},
1016         {"-zoom",  ".zoom", XrmoptionSepArg, 0},
1017         {"-box",  ".box", XrmoptionSepArg, 0},
1018         {"-slow",  ".slow", XrmoptionSepArg, 0},
1019         {"-freeze",  ".freeze", XrmoptionSepArg, 0},
1020         {"-allow2d",  ".allow2d", XrmoptionSepArg, 0},
1021   { 0, 0, 0, 0 }
1022 };
1023
1024 /*
1025 char*        defaults[] =
1026 {
1027         "*rotate:         True",
1028         "*ride:           True",
1029         "*zoom:           True",
1030         "*allow2d:        True",
1031         "*box:            True",
1032         "*slow:           True",
1033         "*freeze:         True",
1034   0
1035 };
1036         */