From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / flow.c
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4 -*- */
2 /* flow --- flow of strange bees */
3
4 #if 0
5 static const char sccsid[] = "@(#)flow.c        5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1996 by Tim Auckland <tda10.geo@yahoo.com>
10  * Incorporating some code from Stephen Davies Copyright (c) 2000
11  *
12  * Search code based on techniques described in "Strange Attractors:
13  * Creating Patterns in Chaos" by Julien C. Sprott
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation.
20  *
21  * This file is provided AS IS with no warranties of any kind.  The author
22  * shall have no liability with respect to the infringement of copyrights,
23  * trade secrets or any patents by this file or any part thereof.  In no
24  * event will the author be liable for any lost revenue or profits or
25  * other special, indirect and consequential damages.
26  *
27  * "flow" shows a variety of continuous phase-space flows around strange
28  * attractors.  It includes the well-known Lorentz mask (the "Butterfly"
29  * of chaos fame), two forms of Rossler's "Folded Band" and Poincare'
30  * sections of the "Birkhoff Bagel" and Duffing's forced occilator.  "flow"
31  * can now discover new attractors.
32  *
33  * Revision History:
34  *
35  * 29-Oct-2004: [TDA] Discover Attractors unknown to science.
36  *   Replace 2D rendering of Periodic Attractors with a 3D
37  *   'interrupted' rendering.  Replace "-/+allow2d" with "-/+periodic"
38  *   Replace all ODE formulae with completely generic forms.
39  *   Add '-search' option to perform background high-speed discovery
40  *   for completely new attractors without impacting rendering
41  *   performance.
42  *   Use gaussian distribution for initial point positions and for
43  *   parameter search.
44  *   Add "+dbuf" option to allow Double-Buffering to be turned off on
45  *   slow X servers.
46  *   Remove redundant '-zoom' option.  Now automatically zooms if both
47  *   rotation and riding are permitted.
48  *   Replace dynamic bounding box with static one pre-calculated
49  *   during discovery phase.
50  *   Simplify and fix bounding box clipping code.  Should now be safe
51  *   to run without double buffer on all XFree86 servers if desired.
52  * 12-Oct-2004: [TDA] Merge Xscreensaver and Xlockmore branches
53  *   Added Chalky's orbital camera, but made the zooming work by
54  *   flying the camera rather than interpolating the view transforms.
55  *   Added Chalky's Bounding Box, but time-averaged the boundaries to
56  *   let the lost bees escape.
57  *   Added Chalky's 'view-frustrum' clipping, but only applying it to
58  *   the Bounding Box.  Trails make clipping less useful.
59  *   Added Chalky's "-slow" and "-freeze" options for compatibility,
60  *   but haven't implemented the features, since the results are ugly
61  *   and make no mathematical contribution.
62  *   Added Double-Buffering as a work-around for a persistent XFree86
63  *   bug that left debris on the screen.
64  * 21-Mar-2003: [TDA] Trails added (XLockmore branch)
65  * 01-Nov-2000: [TDA] Allocation checks (XLockmore branch)
66  * 21-Feb-2000: [Chalky] Major hackage (Stephen Davies, chalky@null.net)
67  *   (Xscreensaver branch)
68  *   Forced perspective mode, added 3d box around attractor which
69  *   involved coding 3d-planar-clipping against the view-frustrum
70  *   thingy. Also made view alternate between piggybacking on a 'bee'
71  *   to zooming around outside the attractor. Most bees slow down and
72  *   stop, to make the structure of the attractor more obvious.
73 * 28-Jan-1999: [TDA] Catch 'lost' bees in flow.c and disable them.
74  *   (XLockmore branch)
75  *   I chose to disable them rather than reinitialise them because
76  *   reinitialising can produce fake attractors.
77  *   This has allowed me to relax some of the parameters and initial
78  *   conditions slightly to catch some of the more extreme cases.  As a
79  *   result you may see some bees fly away at the start - these are the ones
80  *   that 'missed' the attractor.  If the bee with the camera should fly
81  *   away the mode will restart  :-)
82  * 31-Nov-1998: [TDA] Added Duffing  (what a strange day that was :) DAB)
83  *   Duffing's forced oscillator has been added to the formula list and
84  *   the parameters section has been updated to display it in Poincare'
85  *   section.
86  * 30-Nov-1998: [TDA] Added travelling perspective option
87  *   A more exciting point-of-view has been added to all autonomous flows.
88  *   This views the flow as seen by a particle moving with the flow.  In the
89  *   metaphor of the original code, I've attached a camera to one of the
90  *   trained bees!
91  * 30-Nov-1998: [TDA] Much code cleanup.
92  * 09-Apr-1997: [TDA] Ported to xlockmore-4
93  * 18-Jul-1996: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
94  * 31-Aug-1990: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org).
95  */
96
97 #ifdef STANDALONE
98 # define MODE_flow
99 # define DEFAULTS   "*delay:       10000 \n" \
100                                         "*count:       3000  \n" \
101                                         "*size:        -10   \n" \
102                                         "*cycles:      10000 \n" \
103                                         "*ncolors:     200   \n"
104
105 # define release_flow 0
106 # define reshape_flow 0
107 # define flow_handle_event 0
108 # include "xlockmore.h"         /* in xscreensaver distribution */
109 #else /* STANDALONE */
110 # include "xlock.h"             /* in xlockmore distribution */
111 #endif /* STANDALONE */
112
113 #ifdef MODE_flow
114
115 #define DEF_ROTATE   "TRUE"
116 #define DEF_RIDE     "TRUE"
117 #define DEF_BOX      "TRUE"
118 #define DEF_PERIODIC "TRUE"
119 #define DEF_SEARCH   "TRUE"
120 #define DEF_DBUF     "TRUE"
121
122 static Bool rotatep;
123 static Bool ridep;
124 static Bool boxp;
125 static Bool periodicp;
126 static Bool searchp;
127 static Bool dbufp;
128
129 static XrmOptionDescRec opts[] = {
130         {"-rotate",   ".flow.rotate",   XrmoptionNoArg, "on"},
131         {"+rotate",   ".flow.rotate",   XrmoptionNoArg, "off"},
132         {"-ride",     ".flow.ride",     XrmoptionNoArg, "on"},
133         {"+ride",     ".flow.ride",     XrmoptionNoArg, "off"},
134         {"-box",      ".flow.box",      XrmoptionNoArg, "on"},
135         {"+box",      ".flow.box",      XrmoptionNoArg, "off"},
136         {"-periodic", ".flow.periodic", XrmoptionNoArg, "on"},
137         {"+periodic", ".flow.periodic", XrmoptionNoArg, "off"},
138         {"-search",   ".flow.search",   XrmoptionNoArg, "on"},
139         {"+search",   ".flow.search",   XrmoptionNoArg, "off"},
140         {"-dbuf",     ".flow.dbuf",     XrmoptionNoArg, "on"},
141         {"+dbuf",     ".flow.dbuf",     XrmoptionNoArg, "off"},
142 };
143
144 static argtype vars[] = {
145     {&rotatep,   "rotate",   "Rotate",   DEF_ROTATE,   t_Bool},
146     {&ridep,     "ride",     "Ride",     DEF_RIDE,     t_Bool},
147     {&boxp,      "box",      "Box",      DEF_BOX,      t_Bool},
148     {&periodicp, "periodic", "Periodic", DEF_PERIODIC, t_Bool}, 
149     {&searchp,   "search",   "Search",   DEF_SEARCH,   t_Bool}, 
150     {&dbufp,     "dbuf",     "Dbuf",     DEF_DBUF,     t_Bool}, 
151 };
152
153 static OptionStruct desc[] = {
154     {"-/+rotate",   "turn on/off rotating around attractor."},
155     {"-/+ride",     "turn on/off ride in the flow."},
156     {"-/+box",      "turn on/off bounding box."},
157     {"-/+periodic", "turn on/off periodic attractors."},
158     {"-/+search",   "turn on/off search for new attractors."},
159     {"-/+dbuf",     "turn on/off double buffering."},
160 };
161
162 ENTRYPOINT ModeSpecOpt flow_opts =
163 {sizeof opts / sizeof opts[0], opts,
164  sizeof vars / sizeof vars[0], vars, desc};
165
166 #ifdef USE_MODULES
167 ModStruct   flow_description = {
168         "flow", "init_flow", "draw_flow", NULL,
169         "refresh_flow", "init_flow", "free_flow", &flow_opts,
170         1000, 1024, 10000, -10, 200, 1.0, "",
171         "Shows dynamic strange attractors", 0, NULL
172 };
173
174 #endif
175
176 typedef struct { double x, y, z; } dvector;
177
178 #define N_PARS 20 /* Enough for Full Cubic or Periodic Cubic */
179 typedef dvector Par[N_PARS];
180 enum { /* Name the parameter indices to make it easier to write
181                   standard examples */
182         C,
183         X,XX,XXX,XXY,XXZ,XY,XYY,XYZ,XZ,XZZ,
184         Y,YY,YYY,YYZ,YZ,YZZ,
185         Z,ZZ,ZZZ,
186         SINY = XY /* OK to overlap in this case */
187 };
188
189 /* Camera target [TDA] */
190 typedef enum {
191         ORBIT = 0,
192         BEE = 1
193 } Chaseto;
194
195 /* Macros */
196 #define IX(C) ((C) * segindex + sp->cnsegs[(C)])
197 #define B(t,b)  (sp->p + (t) + (b) * sp->taillen)
198 #define X(t,b)  (B((t),(b))->x)
199 #define Y(t,b)  (B((t),(b))->y)
200 #define Z(t,b)  (B((t),(b))->z)
201 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
202 #define LOST_IN_SPACE 2000.0
203 #define INITIALSTEP 0.04
204 #define EYEHEIGHT   0.005
205 #define MINTRAIL 2
206 #define BOX_L 36
207
208 /* Points that make up the box (normalized coordinates) */
209 static const double box[][3] = {
210         {1,1,1},   /* 0 */
211         {1,1,-1},  /* 1 */
212         {1,-1,-1}, /* 2 */
213         {1,-1,1},  /* 3 */
214         {-1,1,1},  /* 4 */
215         {-1,1,-1}, /* 5 */
216         {-1,-1,-1},/* 6 */
217         {-1,-1,1}, /* 7 */
218         {1, .8, .8},
219         {1, .8,-.8},
220         {1,-.8,-.8},
221         {1,-.8, .8},
222         { .8,1, .8},
223         { .8,1,-.8},
224         {-.8,1,-.8},
225         {-.8,1, .8},
226         { .8, .8,1},
227         { .8,-.8,1},
228         {-.8,-.8,1},
229         {-.8, .8,1},
230         {-1, .8, .8},
231         {-1, .8,-.8},
232         {-1,-.8,-.8},
233         {-1,-.8, .8},
234         { .8,-1, .8},
235         { .8,-1,-.8},
236         {-.8,-1,-.8},
237         {-.8,-1, .8},
238         { .8, .8,-1},
239         { .8,-.8,-1},
240         {-.8,-.8,-1},
241         {-.8, .8,-1}
242 };
243
244 /* Lines connecting the box dots */
245 static const double lines[][2] = {
246         {0,1}, {1,2}, {2,3}, {3,0}, /* box */
247         {4,5}, {5,6}, {6,7}, {7,4},
248         {0,4}, {1,5}, {2,6}, {3,7},
249         {4+4,5+4}, {5+4,6+4}, {6+4,7+4}, {7+4,4+4},
250         {4+8,5+8}, {5+8,6+8}, {6+8,7+8}, {7+8,4+8},
251         {4+12,5+12}, {5+12,6+12}, {6+12,7+12}, {7+12,4+12},
252         {4+16,5+16}, {5+16,6+16}, {6+16,7+16}, {7+16,4+16},
253         {4+20,5+20}, {5+20,6+20}, {6+20,7+20}, {7+20,4+20},
254         {4+24,5+24}, {5+24,6+24}, {6+24,7+24}, {7+24,4+24},
255 };
256
257 typedef struct {
258         /* Variables used in rendering */
259         dvector     cam[3]; /* camera flight path */
260         int         chasetime;
261         Chaseto         chaseto;
262         Pixmap      buffer; /* Double Buffer */
263         dvector         circle[2]; /* POV that circles around the scene */
264         dvector     centre;             /* centre */
265         int         beecount;   /* number of bees */
266         XSegment   *csegs;          /* bee lines */
267         int        *cnsegs;
268         XSegment   *old_segs;   /* old bee lines */
269         int         nold_segs;
270         int         taillen;
271
272         /* Variables common to iterators */
273         dvector  (*ODE) (Par par, double x, double y, double z);
274         dvector  range; /* Initial conditions */
275         double   yperiod; /* ODE's where Y is periodic. */
276
277         /* Variables used in iterating main flow */
278         Par         par;
279         dvector    *p;   /* bee positions x[time][bee#] */
280         int         count;
281         double      lyap;
282         double      size;
283         dvector     mid; /* Effective bounding box */
284         double      step;
285         
286         /* second set of variables, used for parallel search */
287         Par         par2;
288         dvector     p2[2];
289         int         count2;
290         double      lyap2;
291         double      size2;
292         dvector     mid2;
293         double      step2;
294         
295 } flowstruct;
296
297 static flowstruct *flows = (flowstruct *) NULL;
298
299 /*
300  * Private functions
301  */
302
303
304 /* ODE functions */
305
306 /* Generic 3D Cubic Polynomial.  Includes all the Quadratics (Lorentz,
307    Rossler) and much more! */
308
309 /* I considered offering a seperate 'Quadratic' option, since Cubic is
310    clearly overkill for the standard examples, but the performance
311    difference is too small to measure.  The compute time is entirely
312    dominated by the XDrawSegments calls anyway. [TDA] */
313 static dvector
314 Cubic(Par a, double x, double y, double z)
315 {
316         dvector d;
317         d.x = a[C].x + a[X].x*x + a[XX].x*x*x + a[XXX].x*x*x*x + a[XXY].x*x*x*y +
318                 a[XXZ].x*x*x*z + a[XY].x*x*y + a[XYY].x*x*y*y + a[XYZ].x*x*y*z +
319                 a[XZ].x*x*z + a[XZZ].x*x*z*z + a[Y].x*y + a[YY].x*y*y +
320                 a[YYY].x*y*y*y + a[YYZ].x*y*y*z + a[YZ].x*y*z + a[YZZ].x*y*z*z +
321                 a[Z].x*z + a[ZZ].x*z*z + a[ZZZ].x*z*z*z;
322
323         d.y = a[C].y + a[X].y*x + a[XX].y*x*x + a[XXX].y*x*x*x + a[XXY].y*x*x*y +
324                 a[XXZ].y*x*x*z + a[XY].y*x*y + a[XYY].y*x*y*y + a[XYZ].y*x*y*z +
325                 a[XZ].y*x*z + a[XZZ].y*x*z*z + a[Y].y*y + a[YY].y*y*y +
326                 a[YYY].y*y*y*y + a[YYZ].y*y*y*z + a[YZ].y*y*z + a[YZZ].y*y*z*z +
327                 a[Z].y*z + a[ZZ].y*z*z + a[ZZZ].y*z*z*z;
328
329         d.z = a[C].z + a[X].z*x + a[XX].z*x*x + a[XXX].z*x*x*x + a[XXY].z*x*x*y +
330                 a[XXZ].z*x*x*z + a[XY].z*x*y + a[XYY].z*x*y*y + a[XYZ].z*x*y*z +
331                 a[XZ].z*x*z + a[XZZ].z*x*z*z + a[Y].z*y + a[YY].z*y*y +
332                 a[YYY].z*y*y*y + a[YYZ].z*y*y*z + a[YZ].z*y*z + a[YZZ].z*y*z*z +
333                 a[Z].z*z + a[ZZ].z*z*z + a[ZZZ].z*z*z*z;
334
335         return d;
336 }
337
338 /* 3D Cubic in (x,z) with periodic sinusoidal forcing term in x.  y is
339    the independent periodic (time) axis.  This includes Birkhoff's
340    Bagel and Duffing's Attractor */
341 static dvector
342 Periodic(Par a, double x, double y, double z)
343 {
344         dvector d;
345
346         d.x = a[C].x + a[X].x*x + a[XX].x*x*x + a[XXX].x*x*x*x +
347                 a[XXZ].x*x*x*z + a[XZ].x*x*z + a[XZZ].x*x*z*z + a[Z].x*z +
348                 a[ZZ].x*z*z + a[ZZZ].x*z*z*z + a[SINY].x*sin(y);
349
350         d.y = a[C].y;
351
352         d.z = a[C].z + a[X].z*x + a[XX].z*x*x + a[XXX].z*x*x*x +
353                 a[XXZ].z*x*x*z + a[XZ].z*x*z + a[XZZ].z*x*z*z + a[Z].z*z +
354                 a[ZZ].z*z*z + a[ZZZ].z*z*z*z;
355
356         return d;
357 }
358
359 /* Numerical integration of the ODE using 2nd order Runge Kutta.
360    Returns length^2 of the update, so that we can detect if the step
361    size needs reducing. */
362 static double
363 Iterate(dvector *p, dvector(*ODE)(Par par, double x, double y, double z),
364                 Par par, double step)
365
366         dvector     k1, k2, k3;
367                         
368         k1 = ODE(par, p->x, p->y, p->z);
369         k1.x *= step;
370         k1.y *= step;
371         k1.z *= step;
372         k2 = ODE(par, p->x + k1.x, p->y + k1.y, p->z + k1.z);
373         k2.x *= step;
374         k2.y *= step;
375         k2.z *= step;
376         k3.x = (k1.x + k2.x) / 2.0;
377         k3.y = (k1.y + k2.y) / 2.0;
378         k3.z = (k1.z + k2.z) / 2.0;
379
380         p->x += k3.x;
381         p->y += k3.y;
382         p->z += k3.z;
383
384         return k3.x*k3.x + k3.y*k3.y + k3.z*k3.z;
385 }
386
387 /* Memory functions */
388
389 #define deallocate(p,t) if (p!=NULL) {free(p); p=(t*)NULL; }
390 #define allocate(p,t,s) if ((p=(t*)malloc(sizeof(t)*s))==NULL)\
391 {free_flow(mi);return;}
392
393 ENTRYPOINT void
394 free_flow(ModeInfo * mi)
395 {
396         flowstruct *sp = &flows[MI_SCREEN(mi)];
397         deallocate(sp->csegs, XSegment);
398         deallocate(sp->cnsegs, int);
399         deallocate(sp->old_segs, XSegment);
400         deallocate(sp->p, dvector);
401 }
402
403 /* Generate Gaussian random number: mean 0, "amplitude" A (actually
404    A is 3*standard deviation). */
405
406 /* Note this generates a pair of gaussian variables, so it saves one
407    to give out next time it's called */
408 static double
409 Gauss_Rand(double A)
410 {
411         static double d;
412         static Bool ready = 0;
413         if(ready) {
414                 ready = 0;
415                 return A/3 * d;
416         } else {
417                 double x, y, w;         
418                 do {
419                         x = 2.0 * (double)LRAND() / MAXRAND - 1.0;
420                         y = 2.0 * (double)LRAND() / MAXRAND - 1.0;
421                         w = x*x + y*y;
422                 } while(w >= 1.0);
423
424                 w = sqrt((-2 * log(w))/w);
425                 ready = 1;              
426                 d =          x * w;
427                 return A/3 * y * w;
428         }
429 }
430
431 /* Attempt to discover new atractors by sending a pair of bees on a
432    fast trip through the new flow and computing their Lyapunov
433    exponent.  Returns False if the bees fly away.
434
435    If the bees stay bounded, the new bounds and the Lyapunov exponent
436    are stored in sp and the function returns True.
437
438    Repeat invocations continue the flow and improve the accuracy of
439    the bounds and the Lyapunov exponent.  Set sp->count2 to zero to
440    start a new test.
441
442    Acts on alternate variable set, so that it can be run in parallel
443    with the main flow */
444
445 static Bool
446 discover(ModeInfo * mi)
447 {
448         flowstruct *sp;
449         double l = 0;
450         dvector dl;
451         dvector max, min;
452         double dl2, df, rs, lsum = 0, s, maxv2 = 0, v2;
453
454         int N, i, nl = 0;
455
456         if (flows == NULL)
457                 return 0;
458         sp = &flows[MI_SCREEN(mi)];
459
460         if(sp->count2 == 0) {
461                 /* initial conditions */
462                 sp->p2[0].x = Gauss_Rand(sp->range.x);
463                 sp->p2[0].y = (sp->yperiod > 0)?
464                         balance_rand(sp->range.y) : Gauss_Rand(sp->range.y);
465                 sp->p2[0].z = Gauss_Rand(sp->range.z);
466                 
467                 /* 1000 steps to find an attractor */
468                 /* Most cases explode out here */
469                 for(N=0; N < 1000; N++){
470                         Iterate(sp->p2, sp->ODE, sp->par2, sp->step2);
471                         if(sp->yperiod > 0 && sp->p2[0].y > sp->yperiod)
472                                 sp->p2[0].y -= sp->yperiod;
473                         if(fabs(sp->p2[0].x) > LOST_IN_SPACE ||
474                            fabs(sp->p2[0].y) > LOST_IN_SPACE ||
475                            fabs(sp->p2[0].z) > LOST_IN_SPACE) {
476                                 return 0;
477                         }
478                         sp->count2++;
479                 }
480                 /* Small perturbation */
481                 sp->p2[1].x = sp->p2[0].x + 0.000001;
482                 sp->p2[1].y = sp->p2[0].y;
483                 sp->p2[1].z = sp->p2[0].z;
484         }
485
486         /* Reset bounding box */
487         max.x = min.x = sp->p2[0].x;
488         max.y = min.y = sp->p2[0].y;
489         max.z = min.z = sp->p2[0].z;
490
491         /* Compute Lyapunov Exponent */
492
493         /* (Technically, we're only estimating the largest Lyapunov
494            Exponent, but that's all we need to know to determine if we
495            have a strange attractor.) [TDA] */
496
497         /* Fly two bees close together */
498         for(N=0; N < 5000; N++){
499                 for(i=0; i< 2; i++) {                   
500                         v2 = Iterate(sp->p2+i, sp->ODE, sp->par2, sp->step2);
501                         if(sp->yperiod > 0 && sp->p2[i].y > sp->yperiod)
502                                 sp->p2[i].y -= sp->yperiod;
503                         
504                         if(fabs(sp->p2[i].x) > LOST_IN_SPACE ||
505                            fabs(sp->p2[i].y) > LOST_IN_SPACE ||
506                            fabs(sp->p2[i].z) > LOST_IN_SPACE) {
507                                 return 0;
508                         }
509                         if(v2 > maxv2) maxv2 = v2; /* Track max v^2 */
510                 }
511
512                 /* find bounding box */
513                 if ( sp->p2[0].x < min.x )      min.x = sp->p2[0].x;
514                 else if ( sp->p2[0].x > max.x ) max.x = sp->p2[0].x;
515                 if ( sp->p2[0].y < min.y )      min.y = sp->p2[0].y;
516                 else if ( sp->p2[0].y > max.y ) max.y = sp->p2[0].y;
517                 if ( sp->p2[0].z < min.z )      min.z = sp->p2[0].z;
518                 else if ( sp->p2[0].z > max.z ) max.z = sp->p2[0].z;
519
520                 /* Measure how much we have to pull the two bees to prevent
521                    them diverging. */
522                 dl.x = sp->p2[1].x - sp->p2[0].x;
523                 dl.y = sp->p2[1].y - sp->p2[0].y;
524                 dl.z = sp->p2[1].z - sp->p2[0].z;
525                 
526                 dl2 = dl.x*dl.x + dl.y*dl.y + dl.z*dl.z;
527                 if(dl2 > 0) {
528                         df = 1e12 * dl2;
529                         rs = 1/sqrt(df);
530                         sp->p2[1].x = sp->p2[0].x + rs * dl.x;
531                         sp->p2[1].y = sp->p2[0].y + rs * dl.y;
532                         sp->p2[1].z = sp->p2[0].z + rs * dl.z;
533                         lsum = lsum + log(df);
534                         nl = nl + 1;
535                         l = M_LOG2E / 2 * lsum / nl / sp->step2;
536                 }
537                 sp->count2++;
538         }
539         /* Anything that didn't explode has a finite attractor */
540         /* If Lyapunov is negative then it probably hit a fixed point or a
541      * limit cycle.  Positive Lyapunov indicates a strange attractor. */
542
543         sp->lyap2 = l;
544
545         sp->size2 = max.x - min.x;
546         s = max.y - min.y;
547         if(s > sp->size2) sp->size2 = s;
548         s = max.z - min.z;
549         if(s > sp->size2) sp->size2 = s;
550
551         sp->mid2.x = (max.x + min.x) / 2;
552         sp->mid2.y = (max.y + min.y) / 2;
553         sp->mid2.z = (max.z + min.z) / 2;
554
555         if(sqrt(maxv2) > sp->size2 * 0.2) {
556                 /* Flowing too fast, reduce step size.  This
557                    helps to eliminate high-speed limit cycles,
558                    which can show +ve Lyapunov due to integration
559                    inaccuracy. */               
560                 sp->step2 /= 2;         
561         }
562         return 1;
563 }
564
565 /* Sets up initial conditions for a flow without all the extra baggage
566    that goes with init_flow */
567 static void
568 restart_flow(ModeInfo * mi)
569 {
570         flowstruct *sp;
571         int         b;
572
573         if (flows == NULL)
574                 return;
575         sp = &flows[MI_SCREEN(mi)];
576         sp->count = 0;
577
578         /* Re-Initialize point positions, velocities, etc. */
579         for (b = 0; b < sp->beecount; b++) {
580                 X(0, b) = Gauss_Rand(sp->range.x);
581                 Y(0, b) = (sp->yperiod > 0)?
582                         balance_rand(sp->range.y) : Gauss_Rand(sp->range.y);
583                 Z(0, b) = Gauss_Rand(sp->range.z);
584         }
585 }
586
587 /* Returns true if line was behind a clip plane, or it clips the line */
588 /* nx,ny,nz is the normal to the plane.   d is the distance from the origin */
589 /* s and e are the end points of the line to be clipped */
590 static int
591 clip(double nx, double ny, double nz, double d, dvector *s, dvector *e)
592 {
593         int front1, front2;
594         dvector w, p;
595         double t;
596
597         front1 = (nx*s->x + ny*s->y + nz*s->z >= -d);
598         front2 = (nx*e->x + ny*e->y + nz*e->z >= -d);
599         if (!front1 && !front2) return 1;
600         if (front1 && front2) return 0; 
601         w.x = e->x - s->x;
602         w.y = e->y - s->y;
603         w.z = e->z - s->z;
604         
605         /* Find t in line equation */
606         t = ( -d - nx*s->x - ny*s->y - nz*s->z) / ( nx*w.x + ny*w.y + nz*w.z);
607         
608         p.x = s->x + w.x * t;
609         p.y = s->y + w.y * t;
610         p.z = s->z + w.z * t;
611         
612         /* Move clipped point to the intersection */
613         if (front2) {
614                 *s = p;
615         } else {
616                 *e = p;
617         }
618         return 0;
619 }
620
621 /* 
622  * Public functions
623  */
624
625 ENTRYPOINT void
626 init_flow (ModeInfo * mi)
627 {
628         flowstruct *sp;
629         char       *name;
630         
631         MI_INIT (mi, flows);
632         sp = &flows[MI_SCREEN(mi)];
633
634         sp->count2 = 0;
635
636         sp->taillen = MI_SIZE(mi);
637         if (sp->taillen < -MINTRAIL) {
638                 /* Change by sqrt so it seems more variable */
639                 sp->taillen = NRAND((int)sqrt((double) (-sp->taillen - MINTRAIL + 1)));
640                 sp->taillen = sp->taillen * sp->taillen + MINTRAIL;
641         } else if (sp->taillen < MINTRAIL) {
642                 sp->taillen = MINTRAIL;
643         }
644
645         if(!rotatep && !ridep) rotatep = True; /* We need at least one viewpoint */
646
647         /* Start camera at Orbit or Bee */
648         if(rotatep) {
649                 sp->chaseto = ORBIT;
650         } else {
651                 sp->chaseto = BEE;
652         }
653         sp->chasetime = 1; /* Go directly to target */
654
655         sp->lyap = 0;
656         sp->yperiod = 0;
657         sp->step2 = INITIALSTEP;
658
659         /* Zero parameter set */
660         memset(sp->par2, 0, N_PARS * sizeof(dvector));
661
662         /* Set up standard examples */
663         switch (NRAND((periodicp) ? 5 : 3)) {
664         case 0:
665                 /*
666                   x' = a(y - x)
667                   y' = x(b - z) - y
668                   z' = xy - cz
669                  */
670                 name = "Lorentz";
671                 sp->par2[Y].x = 10 + balance_rand(5*0); /* a */
672                 sp->par2[X].x = - sp->par2[Y].x;        /* -a */
673                 sp->par2[X].y = 28 + balance_rand(5*0); /* b */
674                 sp->par2[XZ].y = -1;
675                 sp->par2[Y].y = -1;
676                 sp->par2[XY].z = 1;
677                 sp->par2[Z].z = - 2 + balance_rand(1*0); /* -c */               
678                 break;
679         case 1:
680                 /*
681                   x' = -(y + az)
682                   y' = x + by
683                   z' = c + z(x - 5.7)
684                  */
685                 name = "Rossler";
686                 sp->par2[Y].x = -1;
687                 sp->par2[Z].x = -2 + balance_rand(1); /* a */
688                 sp->par2[X].y = 1;
689                 sp->par2[Y].y = 0.2 + balance_rand(0.1); /* b */
690                 sp->par2[C].z = 0.2 + balance_rand(0.1); /* c */
691                 sp->par2[XZ].z = 1;
692                 sp->par2[Z].z = -5.7;
693                 break;
694         case 2: 
695                 /*
696                   x' = -(y + az)
697                   y' = x + by - cz^2
698                   z' = 0.2 + z(x - 5.7)
699                  */
700                 name = "RosslerCone";
701                 sp->par2[Y].x = -1;
702                 sp->par2[Z].x = -2; /* a */
703                 sp->par2[X].y = 1;
704                 sp->par2[Y].y = 0.2; /* b */
705                 sp->par2[ZZ].y = -0.331 + balance_rand(0.01); /* c */
706                 sp->par2[C].z = 0.2;
707                 sp->par2[XZ].z = 1;
708                 sp->par2[Z].z = -5.7;
709                 break;
710         case 3:
711                 /*
712                   x' = -z + b sin(y)
713                   y' = c
714                   z' = 0.7x + az(0.1 - x^2) 
715                  */
716                 name = "Birkhoff";
717                 sp->par2[Z].x = -1;
718                 sp->par2[SINY].x = 0.35 + balance_rand(0.25); /* b */
719                 sp->par2[C].y = 1.57; /* c */
720                 sp->par2[X].z = 0.7;
721                 sp->par2[Z].z = 1 + balance_rand(0.5); /* a/10 */
722                 sp->par2[XXZ].z = -10 * sp->par2[Z].z; /* -a */
723                 sp->yperiod = 2 * M_PI;
724                 break;
725         default:
726                 /*
727                   x' = -ax - z/2 - z^3/8 + b sin(y)
728                   y' = c
729                   z' = 2x
730                  */
731                 name = "Duffing";
732                 sp->par2[X].x = -0.2 + balance_rand(0.1); /* a */
733                 sp->par2[Z].x = -0.5;
734                 sp->par2[ZZZ].x = -0.125;
735                 sp->par2[SINY].x = 27.0 + balance_rand(3.0); /* b */
736                 sp->par2[C].y = 1.33; /* c */
737                 sp->par2[X].z = 2;
738                 sp->yperiod = 2 * M_PI;
739                 break;
740
741         }
742
743         sp->range.x = 5;
744         sp->range.z = 5;
745
746         if(sp->yperiod > 0) {
747                 sp->ODE = Periodic;
748                 /* periodic flows show either uniform distribution or a
749            snapshot on the 'time' axis */
750                 sp->range.y = NRAND(2)? sp->yperiod : 0;
751         } else {
752                 sp->range.y = 5;
753                 sp->ODE = Cubic;
754         }
755
756         /* Run discoverer to set up bounding box, etc.  Lyapunov will
757            probably be innaccurate, since we're only running it once, but
758            we're using known strange attractors so it should be ok. */
759         discover(mi);
760         if(MI_IS_VERBOSE(mi))
761                 fprintf(stdout,
762                                 "flow: Lyapunov exponent: %g, step: %g, size: %g (%s)\n",
763                                 sp->lyap2, sp->step2, sp->size2, name);
764         /* Install new params */
765         sp->lyap = sp->lyap2;
766         sp->size = sp->size2;
767         sp->mid = sp->mid2;
768         sp->step = sp->step2;
769         memcpy(sp->par, sp->par2, sizeof(sp->par2));
770
771         sp->count2 = 0; /* Reset search */
772
773         sp->beecount = MI_COUNT(mi);
774         if (!sp->beecount) {
775                 sp->beecount = 1; /* The camera requires 1 or more */
776         } else if (sp->beecount < 0) {  /* random variations */
777                 sp->beecount = NRAND(-sp->beecount) + 1; /* Minimum 1 */
778         }
779
780 # ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
781   dbufp = False;
782 # endif
783
784         if(dbufp) { /* Set up double buffer */
785                 if (sp->buffer != None)
786                         XFreePixmap(MI_DISPLAY(mi), sp->buffer);
787                 sp->buffer = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
788                                                                  MI_WIDTH(mi), MI_HEIGHT(mi), MI_DEPTH(mi));
789         } else {
790                 sp->buffer = MI_WINDOW(mi);
791         }
792         /* no "NoExpose" events from XCopyArea wanted */
793         XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
794
795         /* Make sure we're using 'thin' lines */
796         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
797                                            JoinMiter);
798
799         /* Clear the background (may be slow depending on user prefs). */
800         MI_CLEARWINDOW(mi);
801
802         /* Allocate memory. */
803         if (sp->csegs == NULL) {
804                 allocate(sp->csegs, XSegment,
805                                  (sp->beecount + BOX_L) * MI_NPIXELS(mi) * sp->taillen);
806                 allocate(sp->cnsegs, int, MI_NPIXELS(mi));
807                 allocate(sp->old_segs, XSegment, (sp->beecount + BOX_L) * sp->taillen);
808                 allocate(sp->p, dvector, sp->beecount * sp->taillen);
809         }
810
811         /* Initialize point positions, velocities, etc. */
812         restart_flow(mi);
813
814         /* Set up camera tail */
815         X(1, 0) = sp->cam[1].x = 0;
816         Y(1, 0) = sp->cam[1].y = 0;
817         Z(1, 0) = sp->cam[1].z = 0;
818 }
819
820 ENTRYPOINT void
821 draw_flow (ModeInfo * mi)
822 {
823         int         b, i;
824         int         col, begin, end;
825         double      M[3][3]; /* transformation matrix */
826         flowstruct *sp = NULL;
827         int         swarm = 0;
828         int         segindex;
829
830         if (flows == NULL)
831                 return;
832         sp = &flows[MI_SCREEN(mi)];
833         if (sp->csegs == NULL)
834                 return;
835
836 #ifdef HAVE_JWXYZ       /* Don't second-guess Quartz's double-buffering */
837     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
838 #endif
839
840         /* multiplier for indexing segment arrays.  Used in IX macro, etc. */
841         segindex = (sp->beecount + BOX_L) * sp->taillen;
842
843         if(searchp){
844                 if(sp->count2 == 0) { /* start new search */
845                         sp->step2 = INITIALSTEP;
846                          /* Pick random parameters.  Actual range is irrelevant
847                                 since parameter scale determines flow speed but not
848                                 structure. */
849                         for(i=0; i< N_PARS; i++) {
850                                 sp->par2[i].x = Gauss_Rand(1.0);
851                                 sp->par2[i].y = Gauss_Rand(1.0);
852                                 sp->par2[i].z = Gauss_Rand(1.0);
853                         }
854                 }
855                 if(!discover(mi)) { /* Flow exploded, reset. */
856                         sp->count2 = 0;
857                 } else {
858                         if(sp->lyap2 < 0) {
859                                 sp->count2 = 0; /* Attractor found, but it's not strange */
860                         }else if(sp->count2 > 1000000) { /* This one will do */
861                                 sp->count2 = 0; /* Reset search */
862                                 if(MI_IS_VERBOSE(mi))
863                                         fprintf(stdout,
864                                                         "flow: Lyapunov exponent: %g, step: %g, size: %g (unnamed)\n",
865                                                         sp->lyap2, sp->step2, sp->size2);
866                                 /* Install new params */
867                                 sp->lyap = sp->lyap2;
868                                 sp->size = sp->size2;
869                                 sp->mid = sp->mid2;
870                                 sp->step = sp->step2;
871                                 memcpy(sp->par, sp->par2, sizeof(sp->par2));
872
873                                 /* If we're allowed to zoom out, do so now, so that we
874                                    get a look at the new attractor. */
875                                 if(sp->chaseto == BEE && rotatep) {
876                                         sp->chaseto = ORBIT;
877                                         sp->chasetime = 100;
878                                 }
879                                 /* Reset initial conditions, so we don't get
880                                    misleading artifacts in the particle density. */
881                                 restart_flow(mi);
882                         }
883                 }
884         }
885         
886         /* Reset segment buffers */
887         for (col = 0; col < MI_NPIXELS(mi); col++)
888                 sp->cnsegs[col] = 0;
889
890         MI_IS_DRAWN(mi) = True;
891
892         /* Calculate circling POV [Chalky]*/
893         sp->circle[1] = sp->circle[0];
894         sp->circle[0].x = sp->size * 2 * sin(sp->count / 100.0) *
895                 (-0.6 + 0.4 *cos(sp->count / 500.0)) + sp->mid.x;
896         sp->circle[0].y = sp->size * 2 * cos(sp->count / 100.0) *
897                 (0.6 + 0.4 *cos(sp->count / 500.0)) + sp->mid.y;
898         sp->circle[0].z = sp->size * 2 * sin(sp->count / 421.0) + sp->mid.z;
899
900         /* Timed chase instead of Chalkie's Bistable oscillator [TDA] */
901         if(rotatep && ridep) {
902                 if(sp->chaseto == BEE && NRAND(1000) == 0){
903                         sp->chaseto = ORBIT;
904                         sp->chasetime = 100;
905                 }else if(NRAND(4000) == 0){
906                         sp->chaseto = BEE;
907                         sp->chasetime = 100;
908                 }
909         }
910
911         /* Set up orientation matrix */
912         {
913                 double x[3], p[3], x2=0, xp=0;
914                 int j;
915                 
916                 /* Chasetime is here to guarantee the camera makes it all the
917                    way to the target in a finite number of steps. */
918                 if(sp->chasetime > 1)
919                         sp->chasetime--;
920                 
921                 if(sp->chaseto == BEE){
922                         /* Camera Head targets bee 0 */
923                         sp->cam[0].x += (X(0, 0) - sp->cam[0].x)/sp->chasetime;
924                         sp->cam[0].y += (Y(0, 0) - sp->cam[0].y)/sp->chasetime;
925                         sp->cam[0].z += (Z(0, 0) - sp->cam[0].z)/sp->chasetime;
926                         
927                         /* Camera Tail targets previous position of bee 0 */
928                         sp->cam[1].x += (X(1, 0) - sp->cam[1].x)/sp->chasetime;
929                         sp->cam[1].y += (Y(1, 0) - sp->cam[1].y)/sp->chasetime;
930                         sp->cam[1].z += (Z(1, 0) - sp->cam[1].z)/sp->chasetime;
931                         
932                         /* Camera Wing targets bee 1 */
933                         sp->cam[2].x += (X(0, 1) - sp->cam[2].x)/sp->chasetime;
934                         sp->cam[2].y += (Y(0, 1) - sp->cam[2].y)/sp->chasetime;
935                         sp->cam[2].z += (Z(0, 1) - sp->cam[2].z)/sp->chasetime;
936                 } else {
937                         /* Camera Head targets Orbiter */
938                         sp->cam[0].x += (sp->circle[0].x - sp->cam[0].x)/sp->chasetime;
939                         sp->cam[0].y += (sp->circle[0].y - sp->cam[0].y)/sp->chasetime;
940                         sp->cam[0].z += (sp->circle[0].z - sp->cam[0].z)/sp->chasetime;
941                         
942                         /* Camera Tail targets diametrically opposite the middle
943                            of the bounding box from the Orbiter */
944                         sp->cam[1].x += 
945                                 (2*sp->circle[0].x - sp->mid.x - sp->cam[1].x)/sp->chasetime;
946                         sp->cam[1].y +=
947                                 (2*sp->circle[0].y - sp->mid.y - sp->cam[1].y)/sp->chasetime;
948                         sp->cam[1].z +=
949                                 (2*sp->circle[0].z - sp->mid.z - sp->cam[1].z)/sp->chasetime;
950                         /* Camera Wing targets previous position of Orbiter */
951                         sp->cam[2].x += (sp->circle[1].x - sp->cam[2].x)/sp->chasetime;
952                         sp->cam[2].y += (sp->circle[1].y - sp->cam[2].y)/sp->chasetime;
953                         sp->cam[2].z += (sp->circle[1].z - sp->cam[2].z)/sp->chasetime;
954                 }
955                 
956                 /* Viewpoint from Tail of camera */
957                 sp->centre.x=sp->cam[1].x;
958                 sp->centre.y=sp->cam[1].y;
959                 sp->centre.z=sp->cam[1].z;
960                 
961                 /* forward vector */
962                 x[0] = sp->cam[0].x - sp->cam[1].x;
963                 x[1] = sp->cam[0].y - sp->cam[1].y;
964                 x[2] = sp->cam[0].z - sp->cam[1].z;
965                 
966                 /* side */
967                 p[0] = sp->cam[2].x - sp->cam[1].x;
968                 p[1] = sp->cam[2].y - sp->cam[1].y;
969                 p[2] = sp->cam[2].z - sp->cam[1].z;
970
971
972                 /* So long as X and P don't collide, these can be used to form
973                    three mutually othogonal axes: X, (X x P) x X and X x P.
974                    After being normalised to unit length, these form the
975                    Orientation Matrix. */
976                 
977                 for(i=0; i<3; i++){
978                         x2+= x[i]*x[i];    /* X . X */
979                         xp+= x[i]*p[i];    /* X . P */
980                         M[0][i] = x[i];    /* X */
981                 }
982                 
983                 for(i=0; i<3; i++)               /* (X x P) x X */
984                         M[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
985                 
986                 M[2][0] =  x[1]*p[2] - x[2]*p[1]; /* X x P */
987                 M[2][1] = -x[0]*p[2] + x[2]*p[0];
988                 M[2][2] =  x[0]*p[1] - x[1]*p[0];
989                 
990                 /* normalise axes */
991                 for(j=0; j<3; j++){
992                         double A=0;
993                         for(i=0; i<3; i++) A+=M[j][i]*M[j][i]; /* sum squares */
994                         A=sqrt(A);
995                         if(A>0)
996                                 for(i=0; i<3; i++) M[j][i]/=A;
997                 }
998
999                 if(sp->chaseto == BEE) {
1000                         X(0, 1)=X(0, 0)+M[1][0]*sp->step; /* adjust neighbour */
1001                         Y(0, 1)=Y(0, 0)+M[1][1]*sp->step;
1002                         Z(0, 1)=Z(0, 0)+M[1][2]*sp->step;
1003                 }
1004         }
1005
1006         /* <=- Bounding Box -=> */
1007         if(boxp) {
1008                 for (b = 0; b < BOX_L; b++) {
1009
1010                         /* Chalky's clipping code, Only used for the box */
1011                         /* clipping trails is slow and of little benefit. [TDA] */
1012                         int p1 = lines[b][0];
1013                         int p2 = lines[b][1];
1014                         dvector A1, A2;
1015                         double x1=box[p1][0]* sp->size/2 + sp->mid.x - sp->centre.x;
1016                         double y1=box[p1][1]* sp->size/2 + sp->mid.y - sp->centre.y;
1017                         double z1=box[p1][2]* sp->size/2 + sp->mid.z - sp->centre.z;
1018                         double x2=box[p2][0]* sp->size/2 + sp->mid.x - sp->centre.x;
1019                         double y2=box[p2][1]* sp->size/2 + sp->mid.y - sp->centre.y;
1020                         double z2=box[p2][2]* sp->size/2 + sp->mid.z - sp->centre.z;
1021                         
1022                         A1.x=M[0][0]*x1 + M[0][1]*y1 + M[0][2]*z1;
1023                         A1.y=M[1][0]*x1 + M[1][1]*y1 + M[1][2]*z1;
1024                         A1.z=M[2][0]*x1 + M[2][1]*y1 + M[2][2]*z1 + EYEHEIGHT * sp->size;
1025                         A2.x=M[0][0]*x2 + M[0][1]*y2 + M[0][2]*z2;
1026                         A2.y=M[1][0]*x2 + M[1][1]*y2 + M[1][2]*z2;
1027                         A2.z=M[2][0]*x2 + M[2][1]*y2 + M[2][2]*z2 + EYEHEIGHT * sp->size;
1028
1029                         /* Clip in 3D before projecting down to 2D.  A 2D clip
1030                            after projection wouldn't be able to handle lines that
1031                            cross x=0 */
1032                         if (clip(1, 0, 0,-1, &A1, &A2) || /* Screen */
1033                                 clip(1, 2, 0, 0, &A1, &A2) || /* Left */
1034                                 clip(1,-2, 0, 0, &A1, &A2) || /* Right */
1035                                 clip(1,0, 2.0*MI_WIDTH(mi)/MI_HEIGHT(mi), 0, &A1, &A2)||/*UP*/
1036                                 clip(1,0,-2.0*MI_WIDTH(mi)/MI_HEIGHT(mi), 0, &A1, &A2))/*Down*/
1037                                 continue;
1038
1039                         /* Colour according to bee */
1040                         col = b % (MI_NPIXELS(mi) - 1);
1041                         
1042                         sp->csegs[IX(col)].x1 = MI_WIDTH(mi)/2 + MI_WIDTH(mi) * A1.y/A1.x;
1043                         sp->csegs[IX(col)].y1 = MI_HEIGHT(mi)/2 + MI_WIDTH(mi) * A1.z/A1.x;
1044                         sp->csegs[IX(col)].x2 = MI_WIDTH(mi)/2 + MI_WIDTH(mi) * A2.y/A2.x;
1045                         sp->csegs[IX(col)].y2 = MI_HEIGHT(mi)/2 + MI_WIDTH(mi) * A2.z/A2.x;
1046                         sp->cnsegs[col]++;
1047                 }
1048         }               
1049
1050         /* <=- Bees -=> */
1051         for (b = 0; b < sp->beecount; b++) {
1052                 if(fabs(X(0, b)) > LOST_IN_SPACE ||
1053                    fabs(Y(0, b)) > LOST_IN_SPACE ||
1054                    fabs(Z(0, b)) > LOST_IN_SPACE){
1055                         if(sp->chaseto == BEE && b == 0){
1056                                 /* Lost camera bee.  Need to replace it since
1057                                    rerunning init_flow could lose us a hard-won new
1058                                    attractor.  Try moving it very close to a random
1059                                    other bee.  This way we have a good chance of being
1060                                    close to the attractor and not forming a false
1061                                    artifact.  If we've lost many bees this may need to
1062                                    be repeated. */
1063                                 /* Don't worry about camera wingbee.  It stays close
1064                                    to the main camera bee no matter what happens. */
1065                                 int newb = 1 + NRAND(sp->beecount - 1);
1066                                 X(0, 0) = X(0, newb) + 0.001;
1067                                 Y(0, 0) = Y(0, newb);
1068                                 Z(0, 0) = Z(0, newb);
1069                                 if(MI_IS_VERBOSE(mi))
1070                                         fprintf(stdout,
1071                                                         "flow: resetting lost camera near bee %d\n",
1072                                                         newb);
1073                         }
1074                         continue;
1075                 }
1076
1077                 /* Age the tail.  It's critical this be fast since
1078                    beecount*taillen can be large. */
1079                 memmove(B(1, b), B(0, b), (sp->taillen - 1) * sizeof(dvector));
1080
1081                 Iterate(B(0,b), sp->ODE, sp->par, sp->step);
1082
1083                 /* Don't show wingbee since he's not quite in the flow. */
1084                 if(sp->chaseto == BEE && b == 1) continue;
1085
1086                 /* Colour according to bee */
1087                 col = b % (MI_NPIXELS(mi) - 1);
1088                 
1089                 /* Fill the segment lists. */
1090                 
1091                 begin = 0; /* begin new trail */
1092                 end = MIN(sp->taillen, sp->count); /* short trails at first */
1093                 for(i=0; i < end; i++){
1094                         double x = X(i,b)-sp->centre.x;
1095                         double y = Y(i,b)*(sp->yperiod < 0? (sp->size/sp->yperiod) :1)
1096                                 -sp->centre.y;
1097                         double z = Z(i,b)-sp->centre.z;
1098                         double XM=M[0][0]*x + M[0][1]*y + M[0][2]*z;
1099                         double YM=M[1][0]*x + M[1][1]*y + M[1][2]*z;
1100                         double ZM=M[2][0]*x + M[2][1]*y + M[2][2]*z + EYEHEIGHT * sp->size;
1101                         short absx, absy;
1102                                                 
1103                         swarm++; /* count the remaining bees */
1104                         if(sp->yperiod > 0 && Y(i,b) > sp->yperiod){
1105                                 int j;
1106                                 Y(i,b) -= sp->yperiod;
1107                                 /* hide tail to prevent streaks in Y.  Streaks in X,Z
1108                                    are ok, they help to outline the Poincare'
1109                                    slice. */
1110                                 for(j = i; j < end; j++) Y(j,b) = Y(i,b);
1111                                 /*begin = i + 1;*/
1112                                 break;
1113                         }
1114                         
1115                         if(XM <= 0){ /* off screen - new trail */
1116                                 begin = i + 1;
1117                                 continue;
1118                         }
1119                         absx = MI_WIDTH(mi)/2 + MI_WIDTH(mi) * YM/XM;
1120                         absy = MI_HEIGHT(mi)/2 + MI_WIDTH(mi) * ZM/XM;
1121                         /* Performance bottleneck */
1122                         if(absx <= 0 || absx >= MI_WIDTH(mi) ||
1123                            absy <= 0 || absy >= MI_HEIGHT(mi)) {
1124                                 /* off screen - new trail */
1125                                 begin = i + 1;
1126                                 continue;
1127                         }
1128                         if(i > begin) {  /* complete previous segment */
1129                                 sp->csegs[IX(col)].x2 = absx;
1130                                 sp->csegs[IX(col)].y2 = absy;
1131                                 sp->cnsegs[col]++;
1132                         }
1133                         
1134                         if(i < end -1){  /* start new segment */
1135                                 sp->csegs[IX(col)].x1 = absx;
1136                                 sp->csegs[IX(col)].y1 = absy;
1137                         }
1138                 }
1139         }
1140
1141          /* Erase */
1142         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
1143         if (dbufp) { /* In Double Buffer case, prepare off-screen copy */
1144                 /* For slow systems, this can be the single biggest bottleneck
1145                    in the program.  These systems may be better of not using
1146                    the double buffer. */ 
1147                 XFillRectangle(MI_DISPLAY(mi), sp->buffer, MI_GC(mi), 0, 0,
1148                                            MI_WIDTH(mi), MI_HEIGHT(mi));
1149         } else { /* Otherwise, erase previous segment list directly */
1150                 XDrawSegments(MI_DISPLAY(mi), sp->buffer, MI_GC(mi),
1151                                           sp->old_segs, sp->nold_segs);
1152         }
1153
1154         /* Render */
1155         if (MI_NPIXELS(mi) > 2){ /* colour */
1156                 int mn  = 0;
1157                 for (col = 0; col < MI_NPIXELS(mi) - 1; col++)
1158                         if (sp->cnsegs[col] > 0) {
1159                                 if(sp->cnsegs[col] > mn) mn = sp->cnsegs[col];
1160                                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, col+1));
1161                                 /* This is usually the biggest bottleneck on most
1162                                    systems.  The maths load is insignificant compared
1163                                    to this.  */
1164                                 XDrawSegments(MI_DISPLAY(mi), sp->buffer, MI_GC(mi),
1165                                                           sp->csegs + col * segindex, sp->cnsegs[col]);
1166                         }
1167         } else { /* mono handled seperately since xlockmore uses '1' for
1168                                 mono white! */
1169                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
1170                 XDrawSegments(MI_DISPLAY(mi), sp->buffer, MI_GC(mi),
1171                                           sp->csegs, sp->cnsegs[0]);
1172         }
1173         if (dbufp) { /* In Double Buffer case, this updates the screen */
1174                 XCopyArea(MI_DISPLAY(mi), sp->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
1175                                   MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
1176         } else { /* Otherwise, screen is already updated.  Copy segments
1177                                 to erase-list to be erased directly next time. */
1178                 int c = 0;
1179                 for (col = 0; col < MI_NPIXELS(mi) - 1; col++) {
1180                         memcpy(sp->old_segs + c, sp->csegs + col * segindex,
1181                                    sp->cnsegs[col] * sizeof(XSegment));
1182                         c += sp->cnsegs[col];
1183                 }
1184                 sp->nold_segs = c;
1185         }
1186
1187         if(sp->count > 1 && swarm == 0) { /* all gone */
1188                 if(MI_IS_VERBOSE(mi))
1189                         fprintf(stdout, "flow: all gone at %d\n", sp->count);
1190                 init_flow(mi);
1191         }
1192
1193         if(sp->count++ > MI_CYCLES(mi)){ /* Time's up.  If we haven't
1194                                                                                 found anything new by now we
1195                                                                                 should pick a new standard
1196                                                                                 flow */
1197                 init_flow(mi);
1198         }
1199 }
1200
1201 #ifndef STANDALONE
1202 ENTRYPOINT void
1203 refresh_flow (ModeInfo * mi)
1204 {
1205         if(!dbufp) MI_CLEARWINDOW(mi);
1206 }
1207 #endif
1208
1209 XSCREENSAVER_MODULE ("Flow", flow)
1210
1211 #endif /* MODE_flow */