http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / flow.c
index 20554824b700bf692d626ada548a2a5d1204f566..08bd2b046ed7cc723f2865d2d5d109c46f9c5ca7 100644 (file)
@@ -1,13 +1,13 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* flow --- flow of strange bees */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
-
 #endif
 
 /*-
  * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Sun.COM>
+ * Portions added by Stephen Davies are Copyright (c) 2000 Stephen Davies
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted,
@@ -23,11 +23,27 @@ static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
  *
  * "flow" shows a variety of continuous phase-space flows around strange
  * attractors.  It includes the well-known Lorentz mask (the "Butterfly"
- * of chaos fame), two forms of Rossler's "Folded Band" and Poincare'
- * section of the "Bagel".
+ * of chaos fame), two forms of Rossler's "Folded Band" and Poincare'
+ * sections of the "Birkhoff Bagel" and Duffing's forced occilator.
  *
  * Revision History:
- * 09-Apr-97: Ported to xlockmore-4
+ * 21-Feb-00: Major hackage by Chalky (Stephen Davies, chalky@null.net)
+ *            Forced perspective mode, added 3d box around attractor which
+ *            involved coding 3d-planar-clipping against the view-frustrum
+ *            thingy. Also made view alternate between piggybacking on a 'bee'
+ *            to zooming around outside the attractor. Most bees slow down and
+ *            stop, to make the structure of the attractor more obvious.
+ * 31-Nov-98: [TDA] Added Duffing  (what a strange day that was :) DAB)
+ *   Duffing's forced oscillator has been added to the formula list and
+ *   the parameters section has been updated to display it in Poincare'
+ *   section.
+ * 30-Nov-98: [TDA] Added travelling perspective option
+ *   A more exciting point-of-view has been added to all autonomous flows.
+ *   This views the flow as seen by a particle moving with the flow.  In the
+ *   metaphor of the original code, I've attached a camera to one of the
+ *   trained bees!
+ * 30-Nov-98: [TDA] Much code cleanup.
+ * 09-Apr-97: [TDA] Ported to xlockmore-4
  * 18-Jul-96: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
  * 31-Aug-90: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
  */
@@ -38,9 +54,16 @@ static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
 # define HACK_DRAW     draw_flow
 # define flow_opts     xlockmore_opts
 # define DEFAULTS      "*delay:                1000 \n" \
-                                       "*count:                1024 \n" \
+                                       "*count:                500 \n" \
                                        "*cycles:               3000 \n" \
-                                       "*ncolors:              200 \n"
+                                       "*ncolors:              200 \n" \
+       "*rotate:         True \n" \
+       "*ride:           True \n" \
+       "*zoom:           True \n" \
+       "*allow2d:        True \n" \
+       "*box:            True \n" \
+       "*slow:           True \n" \
+       "*freeze:         True \n"
 # define SMOOTH_COLORS
 # include "xlockmore.h"                /* in xscreensaver distribution */
 # include "erase.h"
@@ -49,20 +72,19 @@ static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
 # include "xlock.h"            /* in xlockmore distribution */
 #endif /* STANDALONE */
 
-ModeSpecOpt flow_opts =
-{0, NULL, 0, NULL, NULL};
+XrmOptionDescRec flow_options[];
+ModeSpecOpt flow_opts = { 7, flow_options, 0, NULL, NULL };
 
 #ifdef USE_MODULES
-ModStruct   flow_description =
-{"flow", "init_flow", "draw_flow", "release_flow",
- "refresh_flow", "init_flow", NULL, &flow_opts,
- 1000, 1024, 3000, 1, 64, 1.0, "",
- "Shows dynamic strange attractors", 0, NULL};
+ModStruct   flow_description = {
+       "flow", "init_flow", "draw_flow", "release_flow",
+       "refresh_flow", "init_flow", NULL, &flow_opts,
+       1000, 1024, 3000, 1, 64, 1.0, "",
+       "Shows dynamic strange attractors", 0, NULL
+};
 
 #endif
 
-#define TIMES  2               /* number of time positions recorded */
-
 typedef struct {
        double      x;
        double      y;
@@ -74,89 +96,127 @@ typedef struct {
 } Par;
 
 /* Macros */
-#define X(t,b) (sp->p[(t)*sp->beecount+(b)].x)
-#define Y(t,b) (sp->p[(t)*sp->beecount+(b)].y)
-#define Z(t,b) (sp->p[(t)*sp->beecount+(b)].z)
-#define balance_rand(v)        ((LRAND()/MAXRAND*(v))-((v)/2))         /* random number around 0 */
+#define X(t,b) (sp->p[t][b].x)
+#define Y(t,b) (sp->p[t][b].y)
+#define Z(t,b) (sp->p[t][b].z)
+#define balance_rand(v)        ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
+#define SCALE_X(A) (sp->width/2+sp->width/sp->size*(A))
+/*#define SCALE_Y(A) (sp->height/2+sp->height/sp->size*(A))*/
+#define SCALE_Y(A) (sp->height/2+sp->width/sp->size*(A))
+
+/* Mode of operation. Rotate, ride and zoom are mutually exclusive */
+typedef enum {
+       FLOW_ROTATE = 1, /* Rotate around attractor */
+       FLOW_RIDE = 2,   /* Ride a trained bee */
+       FLOW_ZOOM = 4,   /* Zoom in and out */
+       FLOW_2D = 8,     /* Allow 2D attractors */
+       FLOW_BOX = 16,    /* Compute a box around the attractor */
+       FLOW_SLOW = 32,   /* Some bees are slower (and have antifreeze) */
+       FLOW_FREEZE = 64  /* Freeze some of the bees in action */
+} FlowMode;
+
+#define FLOW_DEFAULT (FLOW_ROTATE|FLOW_RIDE|FLOW_ZOOM|FLOW_2D|\
+               FLOW_BOX|FLOW_SLOW|FLOW_FREEZE)
 
 typedef struct {
-       int         pix;
        int         width;
        int         height;
        int         count;
        double      size;
-
+       
        int         beecount;   /* number of bees */
-       XSegment   *csegs;      /* bee lines */
+       XSegment   *csegs;          /* bee lines */
        int        *cnsegs;
        XSegment   *old_segs;   /* old bee lines */
+       int         nold_segs;
        double      step;
-       dvector     c;          /* centre */
-       dvector    *p;          /* bee positions x[time][bee#] */
-       double     *t;
-       double      theta;
-       double      dtheta;
-       double      phi;
-       double      dphi;
-       void        (*ODE) (Par par,
-                                               double *dx, double *dy, double *dz,
-                                               double x,   double y,   double z,
-                                               double t);
+       double          slow;
+       double          slow_view;
+       dvector     centre;             /* centre */
+       dvector         range;
+       struct {
+               double  depth;
+               double  height;
+       }           view;
+       dvector         circle[2]; /* POV that circles around the scene */
+       dvector    *p[2];   /* bee positions x[time][bee#] */
+       struct {
+               double  theta;
+               double  dtheta;
+               double  phi;
+               double  dphi;
+       }           tumble;
+       dvector  (*ODE) (Par par, double x, double y, double z);
        Par         par;
+       FlowMode                mode; /* Mode of operation */
 } flowstruct;
 
 static flowstruct *flows = NULL;
 
-static void
-Lorentz(Par par,
-               double *dx, double *dy, double *dz,
-               double x,   double y,   double z,
-               double t)
+static dvector
+Lorentz(Par par, double x, double y, double z)
 {
-       *dx = par.a * (y - x);
-       *dy = x * (par.b - z) - y;
-       *dz = x * y - par.c * z;
+       dvector d;
+
+       d.x = par.a * (y - x);
+       d.y = x * (par.b - z) - y;
+       d.z = x * y - par.c * z;
+       return d;
 }
 
-static void
-Rossler(Par par,
-               double *dx, double *dy, double *dz,
-               double x,   double y,   double z,
-               double t)
+static dvector
+Rossler(Par par, double x, double y, double z)
 {
-       *dx = -(y + par.a * z);
-       *dy = x + y * par.b;
-       *dz = par.c + z * (x - 5.7);
+       dvector d;
+
+       d.x = -(y + par.a * z);
+       d.y = x + y * par.b;
+       d.z = par.c + z * (x - 5.7);
+       return d;
 }
 
-static void
-RosslerCone(Par par,
-                       double *dx, double *dy, double *dz,
-                       double x,   double y,   double z,
-                       double t)
+static dvector
+RosslerCone(Par par, double x, double y, double z)
 {
-       *dx = -(y + par.a * z);
-       *dy = x + y * par.b - z * z * par.c;
-       *dz = 0.2 + z * (x - 5.7);
+       dvector d;
+
+       d.x = -(y + par.a * z);
+       d.y = x + y * par.b - z * z * par.c;
+       d.z = 0.2 + z * (x - 5.7);
+       return d;
 }
 
-static void
-Bagel(Par par,
-         double *dx, double *dy, double *dz,
-         double x,   double y,   double z,
-         double t)
+static dvector
+Birkhoff(Par par, double x, double y, double z)
 {
-       *dx = -y + par.b * sin(par.c * t);
-       *dy = 0.7 * x + par.a * y * (0.1 - x * x);
-       *dz = 0;
+       dvector d;
+
+       d.x = -y + par.b * sin(z);
+       d.y = 0.7 * x + par.a * y * (0.1 - x * x);
+       d.z = par.c;
+       return d;
 }
 
+static dvector
+Duffing(Par par, double x, double y, double z)
+{
+       dvector d;
+
+       d.x = -par.a * x - y/2 - y * y * y/8 + par.b * cos(z);
+       d.y = 2*x;
+       d.z = par.c;
+       return d;
+}
+
+void init_clip(flowstruct *sp);
+
 void
 init_flow(ModeInfo * mi)
 {
        flowstruct *sp;
        int         b;
-       dvector     range;
+       double      beemult = 1;
+       static int  allocated = 0;
 
        if (flows == NULL) {
                if ((flows = (flowstruct *) calloc(MI_NUM_SCREENS(mi),
@@ -165,9 +225,147 @@ init_flow(ModeInfo * mi)
        }
        sp = &flows[MI_SCREEN(mi)];
 
-       sp->beecount = MI_COUNT(mi);
-       if (sp->beecount < 0) {
-               /* if sp->beecount is random ... the size can change */
+       sp->count = 0;
+       sp->slow = 0.999;
+       sp->slow_view = 0.90;
+
+       sp->width = MI_WIDTH(mi);
+       sp->height = MI_HEIGHT(mi);
+
+       sp->tumble.theta = balance_rand(M_PI);
+       sp->tumble.phi = balance_rand(M_PI);
+       sp->tumble.dtheta = 0.002;
+       sp->tumble.dphi = 0.001;
+       sp->view.height = 0;
+       sp->view.depth = 0; /* no perspective view */
+       sp->mode = 0;
+   if (get_boolean_resource ("rotate", "Boolean")) sp->mode |= FLOW_ROTATE;
+   if (get_boolean_resource ("ride", "Boolean")) sp->mode |= FLOW_RIDE;
+   if (get_boolean_resource ("zoom", "Boolean")) sp->mode |= FLOW_ZOOM;
+   if (get_boolean_resource ("allow2d", "Boolean")) sp->mode |= FLOW_2D;
+   if (get_boolean_resource ("slow", "Boolean")) sp->mode |= FLOW_SLOW;
+   if (get_boolean_resource ("freeze", "Boolean")) sp->mode |= FLOW_FREEZE;
+   if (get_boolean_resource ("box", "Boolean")) sp->mode |= FLOW_BOX;
+
+       b = (sp->mode & FLOW_2D) ? 5 : 3;
+       b = NRAND(b);
+
+       /* If more than one of rotate, ride and zoom are set, choose one */
+       if (b < 3) {
+               int num = 0, modes[3];
+
+               if (sp->mode & FLOW_ROTATE) modes[num++] = FLOW_ROTATE;
+               if (sp->mode & FLOW_RIDE) modes[num++] = FLOW_RIDE;
+               if (sp->mode & FLOW_ZOOM) modes[num++] = FLOW_ZOOM;
+
+               sp->mode &= ~(FLOW_ROTATE | FLOW_RIDE | FLOW_ZOOM);
+
+               if (num) sp->mode |= modes[ NRAND(num) ];
+               else sp->mode |= FLOW_ZOOM;
+       }
+       
+       switch (b) {
+       case 0:
+               sp->view.depth = 10;
+               sp->view.height = 0.2;
+               beemult = 3;
+               sp->ODE = Lorentz;
+               sp->step = 0.02;
+               sp->size = 60;
+               sp->centre.x = 0;
+               sp->centre.y = 0;
+               sp->centre.z = 24;
+               sp->range.x = 5;
+               sp->range.y = 5;
+               sp->range.z = 1;
+               sp->par.a = 10 + balance_rand(5);
+               sp->par.b = 28 + balance_rand(5);
+               sp->par.c = 2 + balance_rand(1);
+               break;
+       case 1:
+               sp->view.depth = 10;
+               sp->view.height = 0.1;
+               beemult = 4;
+               sp->ODE = Rossler;
+               sp->step = 0.05;
+               sp->size = 24;
+               sp->centre.x = 0;
+               sp->centre.y = 0;
+               sp->centre.z = 3;
+               sp->range.x = 4;
+               sp->range.y = 4;
+               sp->range.z = 7;
+               sp->par.a = 2 + balance_rand(1);
+               sp->par.b = 0.2 + balance_rand(0.1);
+               sp->par.c = 0.2 + balance_rand(0.1);
+               break;
+       case 2:
+               sp->view.depth = 10;
+               sp->view.height = 0.1;
+               beemult = 3;
+               sp->ODE = RosslerCone;
+               sp->step = 0.05;
+               sp->size = 24;
+               sp->centre.x = 0;
+               sp->centre.y = 0;
+               sp->centre.z = 3;
+               sp->range.x = 4;
+               sp->range.y = 4;
+               sp->range.z = 4;
+               sp->par.a = 2;
+               sp->par.b = 0.2;
+               sp->par.c = 0.25 + balance_rand(0.09);
+               break;
+       case 3:
+               sp->ODE = Birkhoff;
+               sp->step = 0.04;
+               sp->size = 2.6;
+               sp->centre.x = 0;
+               sp->centre.y = 0;
+               sp->centre.z = 0;
+               sp->range.x = 3;
+               sp->range.y = 4;
+               sp->range.z = 0;
+               sp->par.a = 10 + balance_rand(5);
+               sp->par.b = 0.35 + balance_rand(0.25);
+               sp->par.c = 1.57;
+               sp->tumble.theta = 0;
+               sp->tumble.phi = 0;
+               sp->tumble.dtheta = 0;
+               sp->tumble.dphi = 0;
+               break;
+       case 4:
+       default:
+               sp->ODE = Duffing;
+               sp->step = 0.02;
+               sp->size = 30;
+               sp->centre.x = 0;
+               sp->centre.y = 0;
+               sp->centre.z = 0;
+               sp->range.x = 20;
+               sp->range.y = 20;
+               sp->range.z = 0;
+               sp->par.a = 0.2 + balance_rand(0.1);
+               sp->par.b = 27.0 + balance_rand(3.0);
+               sp->par.c = 1.33;
+               sp->tumble.theta = 0;
+               sp->tumble.phi = 0;
+               sp->tumble.dtheta = -NRAND(2)*sp->par.c*sp->step;
+               sp->tumble.dphi = 0;
+               beemult = 0.5;
+               break;
+       }
+
+       sp->view.depth *= 4;
+
+       sp->beecount = beemult * MI_COUNT(mi);
+       if (sp->beecount < 0)   /* random variations */ 
+               sp->beecount = NRAND(-sp->beecount) + 1; /* Minimum 1 */
+
+       /* Clear the background. */
+       MI_CLEARWINDOW(mi);
+
+       if(!allocated || sp->beecount != allocated){ /* reallocate */
                if (sp->csegs != NULL) {
                        (void) free((void *) sp->csegs);
                        sp->csegs = NULL;
@@ -180,92 +378,17 @@ init_flow(ModeInfo * mi)
                        (void) free((void *) sp->old_segs);
                        sp->old_segs = NULL;
                }
-               if (sp->p != NULL) {
-                       (void) free((void *) sp->p);
-                       sp->p = NULL;
+               if (sp->p[0] != NULL) {
+                       (void) free((void *) sp->p[0]);
+                       sp->p[0] = NULL;
                }
-               if (sp->t != NULL) {
-                       (void) free((void *) sp->t);
-                       sp->t = NULL;
+               if (sp->p[1] != NULL) {
+                       (void) free((void *) sp->p[1]);
+                       sp->p[1] = NULL;
                }
-               sp->beecount = NRAND(-sp->beecount) + 1;        /* Add 1 so its not too boring */
-       }
-       sp->count = 0;
-
-       sp->width = MI_WIDTH(mi);
-       sp->height = MI_HEIGHT(mi);
-
-       sp->theta = balance_rand(M_PI);
-       sp->phi = balance_rand(M_PI);
-       sp->dtheta = 0.002;
-       sp->dphi = 0.001;
-       switch (NRAND(4)) {
-               case 0:
-                       sp->ODE = Lorentz;
-                       sp->step = 0.02;
-                       sp->size = 60;
-                       sp->c.x = 0;
-                       sp->c.y = 0;
-                       sp->c.z = 24;
-                       range.x = 5;
-                       range.y = 5;
-                       range.z = 1;
-                       sp->par.a = 10 + balance_rand(5);
-                       sp->par.b = 28 + balance_rand(5);
-                       sp->par.c = 2 + balance_rand(1);
-                       break;
-               case 1:
-                       sp->ODE = Rossler;
-                       sp->step = 0.05;
-                       sp->size = 24;
-                       sp->c.x = 0;
-                       sp->c.y = 0;
-                       sp->c.z = 3;
-                       range.x = 4;
-                       range.y = 4;
-                       range.z = 7;
-                       sp->par.a = 2 + balance_rand(1);
-                       sp->par.b = 0.2 + balance_rand(0.1);
-                       sp->par.c = 0.2 + balance_rand(0.1);
-                       break;
-               case 2:
-                       sp->ODE = RosslerCone;
-                       sp->step = 0.05;
-                       sp->size = 24;
-                       sp->c.x = 0;
-                       sp->c.y = 0;
-                       sp->c.z = 3;
-                       range.x = 4;
-                       range.y = 4;
-                       range.z = 4;
-                       sp->par.a = 2;
-                       sp->par.b = 0.2;
-                       sp->par.c = 0.25 + balance_rand(0.09);
-                       break;
-               case 3:
-               default:
-                       sp->ODE = Bagel;
-                       sp->step = 0.04;
-                       sp->size = 2.6;
-                       sp->c.x = 0 /*-1.0*/ ;
-                       sp->c.y = 0;
-                       sp->c.z = 0;
-                       range.x = 3;
-                       range.y = 4;
-                       range.z = 0;
-                       sp->par.a = 10 + balance_rand(5);
-                       sp->par.b = 0.35 + balance_rand(0.25);
-                       sp->par.c = 1.57;
-                       sp->theta = 0;
-                       sp->phi = 0;
-                       sp->dtheta = 0 /*sp->par.c*sp->step */ ;
-                       sp->dphi = 0;
-                       break;
+               allocated = sp->beecount;
        }
 
-       /* Clear the background. */
-       MI_CLEARWINDOW(mi);
-
        /* Allocate memory. */
 
        if (!sp->csegs) {
@@ -274,23 +397,219 @@ init_flow(ModeInfo * mi)
                sp->cnsegs = (int *) malloc(sizeof (int) * MI_NPIXELS(mi));
 
                sp->old_segs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount);
-               sp->p = (dvector *) malloc(sizeof (dvector) * sp->beecount * TIMES);
-               sp->t = (double *) malloc(sizeof (double) * sp->beecount);
+               sp->p[0] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
+               sp->p[1] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
        }
+
        /* Initialize point positions, velocities, etc. */
 
-       /* bees */
        for (b = 0; b < sp->beecount; b++) {
-               X(0, b) = balance_rand(range.x);
-               X(1, b) = X(0, b);
-               Y(0, b) = balance_rand(range.y);
-               Y(1, b) = Y(0, b);
-               Z(0, b) = balance_rand(range.z);
-               Z(1, b) = Z(0, b);
-               sp->t[b] = 0;
+               X(1, b) = X(0, b) = balance_rand(sp->range.x);
+               Y(1, b) = Y(0, b) = balance_rand(sp->range.y);
+               Z(1, b) = Z(0, b) = balance_rand(sp->range.z);
        }
+
+       init_clip(sp);
+
 }
 
+/* Clipping planes */
+#define PLANES 5
+static double plane_orig[][2][3] = {
+       /* X goes into screen, Y goes right, Z goes down(up?) */
+       /* {Normal}, {Point} */
+       { {1.0, 0, 0}, {0.01, 0, 0} },
+       { {1.0, 1.0, 0.0}, {0, 0, 0} },
+       { {1.0,-1.0, 0.0}, {0, 0, 0} },
+       { {1.0, 0.0, 1.0}, {0, 0, 0} },
+       { {1.0, 0.0,-1.0}, {0, 0, 0} }
+};
+static double plane[PLANES][2][3];
+static double plane_d[PLANES];
+
+#define BOX_P 32
+#define BOX_L 36
+#define MIN_BOX (3)
+#define MAX_BOX (MIN_BOX + BOX_L)
+/* Points that make up the box (normalized coordinates) */
+static double box_orig[][3] = {
+       {1,1,1},   /* 0 */
+       {1,1,-1},  /* 1 */
+       {1,-1,-1}, /* 2 */
+       {1,-1,1},  /* 3 */
+       {-1,1,1},  /* 4 */
+       {-1,1,-1}, /* 5 */
+       {-1,-1,-1},/* 6 */
+       {-1,-1,1}, /* 7 */
+       {1, .8, .8},
+       {1, .8,-.8},
+       {1,-.8,-.8},
+       {1,-.8, .8},
+       { .8,1, .8},
+       { .8,1,-.8},
+       {-.8,1,-.8},
+       {-.8,1, .8},
+       { .8, .8,1},
+       { .8,-.8,1},
+       {-.8,-.8,1},
+       {-.8, .8,1},
+       {-1, .8, .8},
+       {-1, .8,-.8},
+       {-1,-.8,-.8},
+       {-1,-.8, .8},
+       { .8,-1, .8},
+       { .8,-1,-.8},
+       {-.8,-1,-.8},
+       {-.8,-1, .8},
+       { .8, .8,-1},
+       { .8,-.8,-1},
+       {-.8,-.8,-1},
+       {-.8, .8,-1}
+};
+
+/* Container for scaled box points */
+static double box[BOX_P][3];
+
+/* Lines connecting the box dots */
+static double lines[][2] = {
+       {0,1}, {1,2}, {2,3}, {3,0}, /* box */
+       {4,5}, {5,6}, {6,7}, {7,4},
+       {0,4}, {1,5}, {2,6}, {3,7},
+       {4+4,5+4}, {5+4,6+4}, {6+4,7+4}, {7+4,4+4},
+       {4+8,5+8}, {5+8,6+8}, {6+8,7+8}, {7+8,4+8},
+       {4+12,5+12}, {5+12,6+12}, {6+12,7+12}, {7+12,4+12},
+       {4+16,5+16}, {5+16,6+16}, {6+16,7+16}, {7+16,4+16},
+       {4+20,5+20}, {5+20,6+20}, {6+20,7+20}, {7+20,4+20},
+       {4+24,5+24}, {5+24,6+24}, {6+24,7+24}, {7+24,4+24},
+};
+       
+/* Boundaries of bees */
+double xmin, xmax;
+double ymin, ymax;
+double zmin, zmax;
+
+void init_clip(flowstruct *sp)
+{
+       int i;
+
+       /* Scale the planes to the screen. I had to invert the projection
+        * algorithms so that when projected they would be right at the edge of the
+        * screen. */
+
+    /* #### jwz: I'm not really sure what it means when sp->view.depth is 0
+       in here -- what's the right thing to do? */
+
+       double width = (sp->view.depth
+                    ? sp->size/sp->view.depth/2
+                    : 1);
+       double height = (sp->view.depth
+                     ? (sp->size/sp->view.depth/2*
+                        sp->view.height/sp->view.height)
+                     : 1);
+       for (i = 0; i < PLANES; i++) {
+               /* Copy orig planes into planes, expanding <-> clippings */
+               plane[i][0][0] = plane_orig[i][0][0];
+               plane[i][0][1] = plane_orig[i][0][1] / width;
+               plane[i][0][2] = plane_orig[i][0][2] / height;
+               plane[i][1][0] = plane_orig[i][1][0];
+               plane[i][1][1] = plane_orig[i][1][1];
+               plane[i][1][2] = plane_orig[i][1][2];
+               
+               /* Calculate the 'd' part of 'ax + by + cz = d' */
+               plane_d[i] = - plane[i][0][0] * plane[i][1][0];
+               plane_d[i] -= plane[i][0][1] * plane[i][1][1];
+               plane_d[i] -= plane[i][0][2] * plane[i][1][2];
+       }
+       xmin = X(0, i); xmax = X(0,i);
+       ymin = Y(0, i); ymax = Y(0,i);
+       zmin = Z(0, i); zmax = Z(0,i);
+}
+
+/* Scale the box defined above to fit around all points */
+void create_box(flowstruct *sp)
+{
+       int i = MAX_BOX;
+       double xmid, ymid, zmid;
+       double xsize, ysize, zsize;
+       double size;
+
+       /* Count every 5th point for speed.. */
+       for (; i < sp->beecount; i += 5) {
+               if ( X(0,i) < xmin ) xmin = X(0, i);
+               else if ( X(0,i) > xmax ) xmax = X(0, i);
+               if ( Y(0,i) < ymin ) ymin = Y(0, i);
+               else if ( Y(0,i) > ymax ) ymax = Y(0, i);
+               if ( Z(0,i) < zmin ) zmin = Z(0, i);
+               else if ( Z(0,i) > zmax ) zmax = Z(0, i);
+       }
+       xmid = (xmax+xmin)/2;
+       ymid = (ymax+ymin)/2;
+       zmid = (zmax+zmin)/2;
+       xsize = xmax - xmin;
+       ysize = ymax - ymin;
+       zsize = zmax - zmin;
+       size = xsize;
+       if (ysize> size) size = ysize;
+       if (zsize > size) size = zsize;
+       size /= 2;
+
+       /* Scale box */
+       for (i = 0; i < BOX_P; i++) {
+               box[i][0] = box_orig[i][0] * size + xmid;
+               box[i][1] = box_orig[i][1] * size + ymid;
+               box[i][2] = box_orig[i][2] * size + zmid;
+       }
+
+}
+
+/* Returns true if point is infront of the plane (rather than behind) */
+int infront_of(double x, double y, double z, int i)
+{
+       double sum = plane[i][0][0]*x + plane[i][0][1]*y + plane[i][0][2]*z + plane_d[i];
+       return sum >= 0.0;
+}
+
+/* Returns true if line was behind a clip plane, or clips the line */
+int clip(double *x1, double *y1, double *z1, double *x2, double *y2, double *z2)
+{
+       int i;
+       for (i = 0; i < PLANES; i++) {
+               double t;
+               double x, y, z; /* Intersection point */
+               double dx, dy, dz; /* line delta */
+               int front1, front2;
+               front1 = infront_of(*x1, *y1, *z1, i);
+               front2 = infront_of(*x2, *y2, *z2, i);
+               if (!front1 && !front2) return 1;
+               if (front1 && front2) continue;
+
+               dx = *x2 - *x1;
+               dy = *y2 - *y1;
+               dz = *z2 - *z1;
+
+               /* Find t in line equation */
+               t = ( plane_d[i] - 
+                               plane[i][0][0]*(*x1) - plane[i][0][1]*(*y1) - plane[i][0][2]*(*z1) ) 
+                               / 
+                       ( plane[i][0][0]*dx + plane[i][0][1]*dy + plane[i][0][2]*dz );
+
+               x = *x1 + dx * t;
+               y = *y1 + dy * t;
+               z = *z1 + dz * t;
+               /* Make point that was behind to be the intersect */
+               if (front2) {
+                       *x1 = x;
+                       *y1 = y;
+                       *z1 = z;
+               } else {
+                       *x2 = x;
+                       *y2 = y;
+                       *z2 = z;
+               }
+       }
+       return 0;
+}      
+
 
 void
 draw_flow(ModeInfo * mi)
@@ -299,103 +618,363 @@ draw_flow(ModeInfo * mi)
        Window      window = MI_WINDOW(mi);
        GC          gc = MI_GC(mi);
        flowstruct *sp = &flows[MI_SCREEN(mi)];
-       int         b, c;
+       int         b, c, i;
        int         col, ix;
-       double      sint, cost, sinp, cosp;
-
-       sp->theta += sp->dtheta;
-       sp->phi += sp->dphi;
-       sint = sin(sp->theta);
-       cost = cos(sp->theta);
-       sinp = sin(sp->phi);
-       cosp = cos(sp->phi);
+       int                     new_view = 0;
+       double      M[3][3]; /* transformation matrix */
+       double          step_view = sp->step;
+       double          step_bees = sp->step;
+       double          step_slow = sp->step;
+       double          pp, pc;
+
+       create_box(sp);
+
+       if(!sp->view.depth){ /* simple 3D tumble */
+               double      sint, cost, sinp, cosp;
+               sp->tumble.theta += sp->tumble.dtheta;
+               sp->tumble.phi += sp->tumble.dphi;
+               sint = sin(sp->tumble.theta);
+               cost = cos(sp->tumble.theta);
+               sinp = sin(sp->tumble.phi);
+               cosp = cos(sp->tumble.phi);
+               M[0][0]= cost; M[0][1]=-sint*cosp; M[0][2]= sint*sinp;
+               M[1][0]= sint; M[1][1]= cost*cosp; M[1][2]=-cost*sinp;
+               M[2][0]= 0;    M[2][1]= 0;         M[2][2]= 1;
+       } else { /* initialize matrix */
+               M[0][0]= 0; M[0][1]= 0; M[0][2]= 0;
+               M[1][0]= 0; M[1][1]= 0; M[1][2]= 0;
+               M[2][0]= 0; M[2][1]= 0; M[2][2]= 0;
+
+       }
+
        for (col = 0; col < MI_NPIXELS(mi); col++)
                sp->cnsegs[col] = 0;
 
+       MI_IS_DRAWN(mi) = True;
+
+       /* Calculate circling POV */
+       sp->circle[1] = sp->circle[0];
+       sp->circle[0].x = sp->size * 2 * sin(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
+       sp->circle[0].y = sp->size * 2 * cos(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
+       sp->circle[0].z = sp->size * 2 * sin(sp->count / 421.0);
+
+       if (sp->mode & FLOW_ROTATE)
+               pp = 0;
+       else if (sp->mode & FLOW_RIDE)
+               pp = 1;
+       else /* ZOOM */
+               /* Bistable oscillator to switch between the trained bee and the circler */
+               pp = -sin(sin(sin(cos(sp->count / 150.0)*M_PI/2)*M_PI/2)*M_PI/2) *0.5 + 0.5;
+       pc = 1 - pp;
+
+
+       /* Slow down or speed up the bees / view: */
+
+       /* exponentially accelerate towards zero */
+       sp->slow = sp->slow * 1.005 - 0.005; 
+       if (sp->slow < 0) sp->slow = 0;
+
+       sp->slow_view = sp->slow_view * 1.005 - 0.005;
+       if (sp->slow_view < 0) sp->slow_view = 0;
+
+       /* View speeds up, slow bees slow to half speed, and other bees will
+        * actually stop */
+       step_view = step_view * (1.01 - sp->slow_view * sp->slow_view) * 0.2;
+       step_slow = step_slow * (sp->slow + 0.5) / 2;
+       if (sp->mode & FLOW_SLOW)
+               step_bees = step_bees * sp->slow;
+       else
+               step_bees = step_slow;
+
        /* <=- Bees -=> */
        for (b = 0; b < sp->beecount; b++) {
+               /* Calc if this bee is slow. Note normal bees are exempt from
+                * calculations once they slow to half speed, so that they remain as
+                * frozen lines rather than barely-visible points */
+               int slow = ((b & 0x7) == 0);
+               if ( !(sp->mode & FLOW_FREEZE) ) slow = 1;
                /* Age the arrays. */
-               X(1, b) = X(0, b);
-               Y(1, b) = Y(0, b);
-               Z(1, b) = Z(0, b);
-
-               /* 2nd order Kunge Kutta */
-               {
-                       double      k1x, k1y, k1z;
-                       double      k2x, k2y, k2z;
-
-                       sp->t[b] += sp->step;   /* tick */
-                       sp->ODE(sp->par, &k1x, &k1y, &k1z,
-                               X(1, b), Y(1, b), Z(1, b), sp->t[b]);
-                       k1x *= sp->step;
-                       k1y *= sp->step;
-                       k1z *= sp->step;
-                       sp->ODE(sp->par, &k2x, &k2y, &k2z,
-                               X(1, b) + k1x, Y(1, b) + k1y, Z(1, b) + k1z, sp->t[b]);
-                       k2x *= sp->step;
-                       k2y *= sp->step;
-                       k2z *= sp->step;
-                       X(0, b) = X(1, b) + (k1x + k2x) / 2.0;
-                       Y(0, b) = Y(1, b) + (k1y + k2y) / 2.0;
-                       Z(0, b) = Z(1, b) + (k1z + k2z) / 2.0;
-               }
+               if (b < 2 || sp->slow > 0.5 || slow) {
+                       X(1, b) = X(0, b);
+                       Y(1, b) = Y(0, b);
+                       Z(1, b) = Z(0, b);
 
-               /* Fill the segment lists. */
+                       /* 2nd order Kunge Kutta */
+                       {
+                               dvector     k1, k2;
+                               double          step;
 
+                               if (b == 0 || b == 1) {
+                                       step = step_view;
+                               } else if (slow) {
+                                       step = step_slow;
+                               } else {
+                                       step = step_bees;
+                               }
+                               k1 = sp->ODE(sp->par, X(1, b), Y(1, b), Z(1, b));
+                               k1.x *= step;
+                               k1.y *= step;
+                               k1.z *= step;
+                               k2 = sp->ODE(sp->par, X(1, b) + k1.x, Y(1, b) + k1.y, Z(1, b) + k1.z);
+                               k2.x *= step;
+                               k2.y *= step;
+                               k2.z *= step;
+                               X(0, b) = X(1, b) + (k1.x + k2.x) / 2.0;
+                               Y(0, b) = Y(1, b) + (k1.y + k2.y) / 2.0;
+                               Z(0, b) = Z(1, b) + (k1.z + k2.z) / 2.0;
+                       }
+               }
 
-               /* Tumble */
-#define DISPLAYX(A) (sp->width/2+sp->width/sp->size* \
-                    ((X((A),b)-sp->c.x)*cost \
-                     -(Y((A),b)-sp->c.y)*sint*cosp \
-                     +(Z((A),b)-sp->c.z)*sint*sinp))
-#define DISPLAYY(A) (sp->height/2-sp->height/sp->size* \
-                    ((X((A),b)-sp->c.x)*sint \
-                     +(Y((A),b)-sp->c.y)*cost*cosp \
-                     -(Z((A),b)-sp->c.z)*cost*sinp))
 
                /* Colour according to bee */
                col = b % (MI_NPIXELS(mi) - 1);
-
                ix = col * sp->beecount + sp->cnsegs[col];
-               sp->csegs[ix].x1 = DISPLAYX(0);
-               sp->csegs[ix].y1 = DISPLAYY(0);
-               sp->csegs[ix].x2 = DISPLAYX(1);
-               sp->csegs[ix].y2 = DISPLAYY(1);
-               sp->cnsegs[col]++;
+
+               /* Fill the segment lists. */
+
+               if(sp->view.depth) { /* perspective view has special points */
+                       if(b==0){ /* point of view */
+                               sp->centre.x = X(0, b) * pp + sp->circle[0].x * pc;
+                               sp->centre.y = Y(0, b) * pp + sp->circle[0].y * pc;
+                               sp->centre.z = Z(0, b) * pp + sp->circle[0].z * pc;
+                               /*printf("center: (%3.3f,%3.3f,%3.3f)\n",sp->centre.x, sp->centre.y, sp->centre.z);*/
+                       }else if(b==1){ /* neighbour: used to compute local axes */
+                               double x[3], p[3], x2=0, xp=0, C[3][3];
+                               int j;
+
+                               /* forward */                           
+                               x[0] = X(0, 0) - X(1, 0);
+                               x[1] = Y(0, 0) - Y(1, 0);
+                               x[2] = Z(0, 0) - Z(1, 0);
+                       
+                               /* neighbour */
+                               p[0] = X(0, 1) - X(1, 0);
+                               p[1] = Y(0, 1) - Y(1, 0);
+                               p[2] = Z(0, 1) - Z(1, 0);
+
+                               for(i=0; i<3; i++){
+                                       x2+= x[i]*x[i];    /* X . X */
+                                       xp+= x[i]*p[i];    /* X . P */
+                                       M[0][i] = x[i];    /* X */
+                               }
+
+                               for(i=0; i<3; i++)               /* (X x P) x X */
+                                       M[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
+                               
+                               M[2][0] =  x[1]*p[2] - x[2]*p[1]; /* X x P */
+                               M[2][1] = -x[0]*p[2] + x[2]*p[0];
+                               M[2][2] =  x[0]*p[1] - x[1]*p[0];
+
+                               /* normalise axes */
+                               for(j=0; j<3; j++){
+                                       double A=0;
+                                       for(i=0; i<3; i++) A+=M[j][i]*M[j][i]; /* sum squares */
+                                       A=sqrt(A);
+                                       for(i=0; i<3; i++) M[j][i]/=A;
+                               }
+
+                               X(0, 1)=X(0, 0)+M[1][0]; /* adjust neighbour */
+                               Y(0, 1)=Y(0, 0)+M[1][1];
+                               Z(0, 1)=Z(0, 0)+M[1][2];
+
+                               /* Look at trained bee into C matrix */
+                               /* forward */                           
+                               x[0] = 0 - sp->circle[0].x;
+                               x[1] = 0 - sp->circle[0].y;
+                               x[2] = 0 - sp->circle[0].z;
+                       
+                               /* neighbour */
+                               p[0] = sp->circle[0].x - sp->circle[1].x;
+                               p[1] = sp->circle[0].y - sp->circle[1].y;
+                               p[2] = sp->circle[0].z - sp->circle[1].z;
+
+                               for(i=0; i<3; i++){
+                                       x2+= x[i]*x[i];    /* X . X */
+                                       xp+= x[i]*p[i];    /* X . P */
+                                       C[0][i] = x[i];    /* X */
+                               }
+
+                               for(i=0; i<3; i++)               /* (X x P) x X */
+                                       C[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
+                               
+                               C[2][0] =  x[1]*p[2] - x[2]*p[1]; /* X x P */
+                               C[2][1] = -x[0]*p[2] + x[2]*p[0];
+                               C[2][2] =  x[0]*p[1] - x[1]*p[0];
+
+                               /* normalise axes */
+                               for(j=0; j<3; j++){
+                                       double A=0;
+                                       for(i=0; i<3; i++) A+=C[j][i]*C[j][i]; /* sum squares */
+                                       A=sqrt(A);
+                    if (A != 0) /* #### is this right? */
+                      for(i=0; i<3; i++) C[j][i]/=A;
+                               }
+
+                               /* Interpolate between Center and Trained Bee matrices */
+                               /* This isn't very accurate and leads to weird transformations
+                                * (shearing, etc), but it works. Besides, sometimes they look
+                                * cool :) */
+                               pp = pp * pp; /* Don't follow bee's direction until very close */
+                               pc = 1 - pp;
+                               for (i = 0; i < 3; i++)
+                                       for (j = 0; j < 3; j++)
+                                               M[i][j] = M[i][j] * pp + C[i][j] * pc;
+                               
+
+#if 0  /* display local axes for testing */
+                               X(1, b)=X(0, 0);
+                               Y(1, b)=Y(0, 0);
+                               Z(1, b)=Z(0, 0);
+                       }else if(b==2){
+                               X(0, b)=X(0, 0)+0.5*M[0][0];
+                               Y(0, b)=Y(0, 0)+0.5*M[0][1];
+                               Z(0, b)=Z(0, 0)+0.5*M[0][2];
+                               X(1, b)=X(0, 0);
+                               Y(1, b)=Y(0, 0);
+                               Z(1, b)=Z(0, 0);
+                       }else if(b==3){
+                               X(0, b)=X(0, 0)+1.5*M[2][0];
+                               Y(0, b)=Y(0, 0)+1.5*M[2][1];
+                               Z(0, b)=Z(0, 0)+1.5*M[2][2];
+                               X(1, b)=X(0, 0);
+                               Y(1, b)=Y(0, 0);
+                               Z(1, b)=Z(0, 0);
+#endif
+                       /* Draw a box... */
+                       }
+               }
+                       if (b >= MIN_BOX && b < MAX_BOX) {
+                               int p1 = lines[b-MIN_BOX][0];
+                               int p2 = lines[b-MIN_BOX][1];
+                               X(0, b) = box[p1][0]; Y(0, b) = box[p1][1]; Z(0, b) = box[p1][2];
+                               X(1, b) = box[p2][0]; Y(1, b) = box[p2][1]; Z(1, b) = box[p2][2];
+                       }
+               
+               
+#if 0  /* Original code */
+               for(i=0; i<2; i++){
+                       double x=X(i,b)-sp->centre.x;
+                       double y=Y(i,b)-sp->centre.y;
+                       double z=Z(i,b)-sp->centre.z;
+                       double X=M[0][0]*x + M[0][1]*y + M[0][2]*z;
+                       double Y=M[1][0]*x + M[1][1]*y + M[1][2]*z;
+                       double Z=M[2][0]*x + M[2][1]*y + M[2][2]*z+sp->view.height;
+                       double absx, absy;                              
+                       if(sp->view.depth){
+                               if(X <= 0) break;
+                               absx=SCALE_X(sp->view.depth*Y/X);
+                               absy=SCALE_Y(sp->view.depth*Z/X);
+                               if(absx < -sp->width || absx > 2*sp->width ||
+                                  absy < -sp->height || absy > 2*sp->height)
+                                       break;
+                       }else{
+                               absx=SCALE_X(X);
+                               absy=SCALE_Y(Y);
+                       }
+                       if(i){
+                               sp->csegs[ix].x1 = (short) absx;
+                               sp->csegs[ix].y1 = (short) absy;
+                       }else{
+                               sp->csegs[ix].x2 = (short) absx;
+                               sp->csegs[ix].y2 = (short) absy;
+                       }
+               }
+               if(i == 2) /* both assigned */
+                       sp->cnsegs[col]++;
+#else  
+               /* Chalky's code w/ clipping */
+               if (b < ((sp->mode & FLOW_BOX) ? 2 : MAX_BOX))
+                       continue;
+               do {
+                       double x1=X(0,b)-sp->centre.x;
+                       double y1=Y(0,b)-sp->centre.y;
+                       double z1=Z(0,b)-sp->centre.z;
+                       double X1=M[0][0]*x1 + M[0][1]*y1 + M[0][2]*z1;
+                       double Y1=M[1][0]*x1 + M[1][1]*y1 + M[1][2]*z1;
+                       double Z1=M[2][0]*x1 + M[2][1]*y1 + M[2][2]*z1+sp->view.height;
+                       double absx1, absy1;                            
+                       double x2=X(1,b)-sp->centre.x;
+                       double y2=Y(1,b)-sp->centre.y;
+                       double z2=Z(1,b)-sp->centre.z;
+                       double X2=M[0][0]*x2 + M[0][1]*y2 + M[0][2]*z2;
+                       double Y2=M[1][0]*x2 + M[1][1]*y2 + M[1][2]*z2;
+                       double Z2=M[2][0]*x2 + M[2][1]*y2 + M[2][2]*z2+sp->view.height;
+                       double absx2, absy2;                            
+                       if(sp->view.depth){
+                               /* Need clipping if: is part of box, or close to viewer */
+                               if ( (b >= MIN_BOX && b < MAX_BOX) || X1 <= 0.1 || X2 < 0.1) 
+                                       if (clip(&X1, &Y1, &Z1, &X2, &Y2, &Z2))
+                                               break;
+                               if (X1 <= 0 || X2 <= 0) break;
+                               absx1=SCALE_X(sp->view.depth*Y1/X1);
+                               absy1=SCALE_Y(sp->view.depth*Z1/X1);
+                               if(absx1 < -sp->width || absx1 > 2*sp->width ||
+                                  absy1 < -sp->height || absy1 > 2*sp->height)
+                                       break;
+                               absx2=SCALE_X(sp->view.depth*Y2/X2);
+                               absy2=SCALE_Y(sp->view.depth*Z2/X2);
+                               if(absx2 < -sp->width || absx2 > 2*sp->width ||
+                                  absy2 < -sp->height || absy2 > 2*sp->height)
+                                       break;
+                       }else{
+                               absx1=SCALE_X(X1);
+                               absy1=SCALE_Y(Y1);
+                               absx2=SCALE_X(X2);
+                               absy2=SCALE_Y(Y2);
+                       }
+
+                       sp->csegs[ix].x1 = (short) absx1;
+                       sp->csegs[ix].y1 = (short) absy1;
+                       sp->csegs[ix].x2 = (short) absx2;
+                       sp->csegs[ix].y2 = (short) absy2;
+
+                       sp->cnsegs[col]++;
+               } while (0);
+#endif
        }
-       if (sp->count) {
+
+       if (sp->count) { /* erase */
                XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
-               XDrawSegments(display, window, gc, sp->old_segs, sp->beecount);
+               XDrawSegments(display, window, gc, sp->old_segs, sp->nold_segs);
        }
-       XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
-       if (MI_NPIXELS(mi) > 2) {
-               for (col = 0; col < MI_NPIXELS(mi); col++) {
+
+       if (MI_NPIXELS(mi) > 2){ /* render colour */
+               for (col = 0; col < MI_NPIXELS(mi); col++)
                        if (sp->cnsegs[col] > 0) {
                                XSetForeground(display, gc, MI_PIXEL(mi, col));
                                XDrawSegments(display, window, gc,
-                                             sp->csegs + col * sp->beecount,
-                                             sp->cnsegs[col]);
+                                             sp->csegs + col * sp->beecount, sp->cnsegs[col]);
                        }
-               }
-       } else {
-         /* mono */
-         XSetForeground(display, gc, MI_PIXEL(mi, 1));
-         XDrawSegments(display, window, gc,
-                                       sp->csegs + col * sp->beecount,
-                                       sp->cnsegs[col]);
+       } else {                /* render mono */
+               XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
+               XDrawSegments(display, window, gc,
+                                         sp->csegs + col * sp->beecount, sp->cnsegs[col]);
        }
+
+       /* Copy to erase-list */
        for (col = 0, c = 0; col < MI_NPIXELS(mi); col++)
-               for (b = 0; b < sp->cnsegs[col]; b++) {
-                       XSegment    s = (sp->csegs + col * sp->beecount)[b];
-
-                       sp->old_segs[c].x1 = s.x1;
-                       sp->old_segs[c].y1 = s.y1;
-                       sp->old_segs[c].x2 = s.x2;
-                       sp->old_segs[c].y2 = s.y2;
-                       c++;
-               }
-       if (++sp->count > MI_CYCLES(mi)) {
+               for (b = 0; b < sp->cnsegs[col]; b++)
+                       sp->old_segs[c++] = (sp->csegs + col * sp->beecount)[b];
+       sp->nold_segs = c;
+
+       if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
                init_flow(mi);
+
+       if (sp->count % (MI_CYCLES(mi)/4) == 0) { /* pick a new view */
+               new_view = 0; /* change to 1 .. */
+       }
+
+       if (X(0, 0) < xmin*2 || X(0, 0) > xmax*2) new_view = 1;
+       if (Y(0, 0) < ymin*2 || Y(0, 0) > ymax*2) new_view = 1;
+       if (Z(0, 0) < zmin*2 || Z(0, 0) > zmax*2) new_view = 1;
+
+       if (new_view) {
+               for (b = 0; b < 2; b++) {
+                       X(1, b) = X(0, b) = balance_rand(sp->range.x*4);
+                       Y(1, b) = Y(0, b) = balance_rand(sp->range.y*4);
+                       Z(1, b) = Z(0, b) = balance_rand(sp->range.z*4);
+               }
+               sp->slow_view = 0.90;
        }
 }
 
@@ -414,10 +993,10 @@ release_flow(ModeInfo * mi)
                                (void) free((void *) sp->cnsegs);
                        if (sp->old_segs != NULL)
                                (void) free((void *) sp->old_segs);
-                       if (sp->p != NULL)
-                               (void) free((void *) sp->p);
-                       if (sp->t != NULL)
-                               (void) free((void *) sp->t);
+                       if (sp->p[0] != NULL)
+                               (void) free((void *) sp->p[0]);
+                       if (sp->p[1] != NULL)
+                               (void) free((void *) sp->p[1]);
                }
                (void) free((void *) flows);
                flows = NULL;
@@ -429,3 +1008,29 @@ refresh_flow(ModeInfo * mi)
 {
        MI_CLEARWINDOW(mi);
 }
+
+XrmOptionDescRec flow_options[] =
+{
+       {"-rotate",  ".rotate", XrmoptionSepArg, 0},
+       {"-ride",  ".ride", XrmoptionSepArg, 0},
+       {"-zoom",  ".zoom", XrmoptionSepArg, 0},
+       {"-box",  ".box", XrmoptionSepArg, 0},
+       {"-slow",  ".slow", XrmoptionSepArg, 0},
+       {"-freeze",  ".freeze", XrmoptionSepArg, 0},
+       {"-allow2d",  ".allow2d", XrmoptionSepArg, 0},
+  { 0, 0, 0, 0 }
+};
+
+/*
+char*        defaults[] =
+{
+       "*rotate:         True",
+       "*ride:           True",
+       "*zoom:           True",
+       "*allow2d:        True",
+       "*box:            True",
+       "*slow:           True",
+       "*freeze:         True",
+  0
+};
+       */