1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* flow --- flow of strange bees */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
10 * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Sun.COM>
11 * Portions added by Stephen Davies are Copyright (c) 2000 Stephen Davies
13 * Permission to use, copy, modify, and distribute this software and its
14 * documentation for any purpose and without fee is hereby granted,
15 * provided that the above copyright notice appear in all copies and that
16 * both that copyright notice and this permission notice appear in
17 * supporting documentation.
19 * This file is provided AS IS with no warranties of any kind. The author
20 * shall have no liability with respect to the infringement of copyrights,
21 * trade secrets or any patents by this file or any part thereof. In no
22 * event will the author be liable for any lost revenue or profits or
23 * other special, indirect and consequential damages.
25 * "flow" shows a variety of continuous phase-space flows around strange
26 * attractors. It includes the well-known Lorentz mask (the "Butterfly"
27 * of chaos fame), two forms of Rossler's "Folded Band" and Poincare'
28 * sections of the "Birkhoff Bagel" and Duffing's forced occilator.
31 * 21-Feb-00: Major hackage by Chalky (Stephen Davies, chalky@null.net)
32 * Forced perspective mode, added 3d box around attractor which
33 * involved coding 3d-planar-clipping against the view-frustrum
34 * thingy. Also made view alternate between piggybacking on a 'bee'
35 * to zooming around outside the attractor. Most bees slow down and
36 * stop, to make the structure of the attractor more obvious.
37 * 31-Nov-98: [TDA] Added Duffing (what a strange day that was :) DAB)
38 * Duffing's forced oscillator has been added to the formula list and
39 * the parameters section has been updated to display it in Poincare'
41 * 30-Nov-98: [TDA] Added travelling perspective option
42 * A more exciting point-of-view has been added to all autonomous flows.
43 * This views the flow as seen by a particle moving with the flow. In the
44 * metaphor of the original code, I've attached a camera to one of the
46 * 30-Nov-98: [TDA] Much code cleanup.
47 * 09-Apr-97: [TDA] Ported to xlockmore-4
48 * 18-Jul-96: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
49 * 31-Aug-90: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
53 # define PROGCLASS "Flow"
54 # define HACK_INIT init_flow
55 # define HACK_DRAW draw_flow
56 # define flow_opts xlockmore_opts
57 # define DEFAULTS "*delay: 1000 \n" \
68 # define SMOOTH_COLORS
69 # include "xlockmore.h" /* in xscreensaver distribution */
72 #else /* STANDALONE */
73 # include "xlock.h" /* in xlockmore distribution */
74 #endif /* STANDALONE */
76 XrmOptionDescRec flow_options[];
77 ModeSpecOpt flow_opts = { 7, flow_options, 0, NULL, NULL };
80 ModStruct flow_description = {
81 "flow", "init_flow", "draw_flow", "release_flow",
82 "refresh_flow", "init_flow", NULL, &flow_opts,
83 1000, 1024, 3000, 1, 64, 1.0, "",
84 "Shows dynamic strange attractors", 0, NULL
100 #define X(t,b) (sp->p[t][b].x)
101 #define Y(t,b) (sp->p[t][b].y)
102 #define Z(t,b) (sp->p[t][b].z)
103 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
104 #define SCALE_X(A) (sp->width/2+sp->width/sp->size*(A))
105 /*#define SCALE_Y(A) (sp->height/2+sp->height/sp->size*(A))*/
106 #define SCALE_Y(A) (sp->height/2+sp->width/sp->size*(A))
108 /* Mode of operation. Rotate, ride and zoom are mutually exclusive */
110 FLOW_ROTATE = 1, /* Rotate around attractor */
111 FLOW_RIDE = 2, /* Ride a trained bee */
112 FLOW_ZOOM = 4, /* Zoom in and out */
113 FLOW_2D = 8, /* Allow 2D attractors */
114 FLOW_BOX = 16, /* Compute a box around the attractor */
115 FLOW_SLOW = 32, /* Some bees are slower (and have antifreeze) */
116 FLOW_FREEZE = 64, /* Freeze some of the bees in action */
119 #define FLOW_DEFAULT (FLOW_ROTATE|FLOW_RIDE|FLOW_ZOOM|FLOW_2D|\
120 FLOW_BOX|FLOW_SLOW|FLOW_FREEZE)
128 int beecount; /* number of bees */
129 XSegment *csegs; /* bee lines */
131 XSegment *old_segs; /* old bee lines */
136 dvector centre; /* centre */
142 dvector circle[2]; /* POV that circles around the scene */
143 dvector *p[2]; /* bee positions x[time][bee#] */
150 dvector (*ODE) (Par par, double x, double y, double z);
152 FlowMode mode; /* Mode of operation */
155 static flowstruct *flows = NULL;
158 Lorentz(Par par, double x, double y, double z)
162 d.x = par.a * (y - x);
163 d.y = x * (par.b - z) - y;
164 d.z = x * y - par.c * z;
169 Rossler(Par par, double x, double y, double z)
173 d.x = -(y + par.a * z);
175 d.z = par.c + z * (x - 5.7);
180 RosslerCone(Par par, double x, double y, double z)
184 d.x = -(y + par.a * z);
185 d.y = x + y * par.b - z * z * par.c;
186 d.z = 0.2 + z * (x - 5.7);
191 Birkhoff(Par par, double x, double y, double z)
195 d.x = -y + par.b * sin(z);
196 d.y = 0.7 * x + par.a * y * (0.1 - x * x);
202 Duffing(Par par, double x, double y, double z)
206 d.x = -par.a * x - y/2 - y * y * y/8 + par.b * cos(z);
212 void init_clip(flowstruct *sp);
215 init_flow(ModeInfo * mi)
220 static int allocated = 0;
223 if ((flows = (flowstruct *) calloc(MI_NUM_SCREENS(mi),
224 sizeof (flowstruct))) == NULL)
227 sp = &flows[MI_SCREEN(mi)];
231 sp->slow_view = 0.90;
233 sp->width = MI_WIDTH(mi);
234 sp->height = MI_HEIGHT(mi);
236 sp->tumble.theta = balance_rand(M_PI);
237 sp->tumble.phi = balance_rand(M_PI);
238 sp->tumble.dtheta = 0.002;
239 sp->tumble.dphi = 0.001;
241 sp->view.depth = 0; /* no perspective view */
243 if (get_boolean_resource ("rotate", "Boolean")) sp->mode |= FLOW_ROTATE;
244 if (get_boolean_resource ("ride", "Boolean")) sp->mode |= FLOW_RIDE;
245 if (get_boolean_resource ("zoom", "Boolean")) sp->mode |= FLOW_ZOOM;
246 if (get_boolean_resource ("allow2d", "Boolean")) sp->mode |= FLOW_2D;
247 if (get_boolean_resource ("slow", "Boolean")) sp->mode |= FLOW_SLOW;
248 if (get_boolean_resource ("freeze", "Boolean")) sp->mode |= FLOW_FREEZE;
249 if (get_boolean_resource ("box", "Boolean")) sp->mode |= FLOW_BOX;
251 b = (sp->mode & FLOW_2D) ? 5 : 3;
254 /* If more than one of rotate, ride and zoom are set, choose one */
256 int num = 0, modes[3];
258 if (sp->mode & FLOW_ROTATE) modes[num++] = FLOW_ROTATE;
259 if (sp->mode & FLOW_RIDE) modes[num++] = FLOW_RIDE;
260 if (sp->mode & FLOW_ZOOM) modes[num++] = FLOW_ZOOM;
262 sp->mode &= ~(FLOW_ROTATE | FLOW_RIDE | FLOW_ZOOM);
264 if (num) sp->mode |= modes[ NRAND(num) ];
265 else sp->mode |= FLOW_ZOOM;
271 sp->view.height = 0.2;
282 sp->par.a = 10 + balance_rand(5);
283 sp->par.b = 28 + balance_rand(5);
284 sp->par.c = 2 + balance_rand(1);
288 sp->view.height = 0.1;
299 sp->par.a = 2 + balance_rand(1);
300 sp->par.b = 0.2 + balance_rand(0.1);
301 sp->par.c = 0.2 + balance_rand(0.1);
305 sp->view.height = 0.1;
307 sp->ODE = RosslerCone;
318 sp->par.c = 0.25 + balance_rand(0.09);
330 sp->par.a = 10 + balance_rand(5);
331 sp->par.b = 0.35 + balance_rand(0.25);
333 sp->tumble.theta = 0;
335 sp->tumble.dtheta = 0;
349 sp->par.a = 0.2 + balance_rand(0.1);
350 sp->par.b = 27.0 + balance_rand(3.0);
352 sp->tumble.theta = 0;
354 sp->tumble.dtheta = -NRAND(2)*sp->par.c*sp->step;
362 sp->beecount = beemult * MI_COUNT(mi);
363 if (sp->beecount < 0) /* random variations */
364 sp->beecount = NRAND(-sp->beecount) + 1; /* Minimum 1 */
366 /* Clear the background. */
369 if(!allocated || sp->beecount != allocated){ /* reallocate */
370 if (sp->csegs != NULL) {
371 (void) free((void *) sp->csegs);
374 if (sp->cnsegs != NULL) {
375 (void) free((void *) sp->cnsegs);
378 if (sp->old_segs != NULL) {
379 (void) free((void *) sp->old_segs);
382 if (sp->p[0] != NULL) {
383 (void) free((void *) sp->p[0]);
386 if (sp->p[1] != NULL) {
387 (void) free((void *) sp->p[1]);
390 allocated = sp->beecount;
393 /* Allocate memory. */
396 sp->csegs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount
398 sp->cnsegs = (int *) malloc(sizeof (int) * MI_NPIXELS(mi));
400 sp->old_segs = (XSegment *) malloc(sizeof (XSegment) * sp->beecount);
401 sp->p[0] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
402 sp->p[1] = (dvector *) malloc(sizeof (dvector) * sp->beecount);
405 /* Initialize point positions, velocities, etc. */
407 for (b = 0; b < sp->beecount; b++) {
408 X(1, b) = X(0, b) = balance_rand(sp->range.x);
409 Y(1, b) = Y(0, b) = balance_rand(sp->range.y);
410 Z(1, b) = Z(0, b) = balance_rand(sp->range.z);
417 /* Clipping planes */
419 static double plane_orig[][2][3] = {
420 /* X goes into screen, Y goes right, Z goes down(up?) */
421 /* {Normal}, {Point} */
422 { {1.0, 0, 0}, {0.01, 0, 0} },
423 { {1.0, 1.0, 0.0}, {0, 0, 0} },
424 { {1.0,-1.0, 0.0}, {0, 0, 0} },
425 { {1.0, 0.0, 1.0}, {0, 0, 0} },
426 { {1.0, 0.0,-1.0}, {0, 0, 0} }
428 static double plane[PLANES][2][3];
429 static double plane_d[PLANES];
434 #define MAX_BOX (MIN_BOX + BOX_L)
435 /* Points that make up the box (normalized coordinates) */
436 static double box_orig[][3] = {
471 /* Container for scaled box points */
472 static double box[BOX_P][3];
474 /* Lines connecting the box dots */
475 static double lines[][2] = {
476 {0,1}, {1,2}, {2,3}, {3,0}, /* box */
477 {4,5}, {5,6}, {6,7}, {7,4},
478 {0,4}, {1,5}, {2,6}, {3,7},
479 {4+4,5+4}, {5+4,6+4}, {6+4,7+4}, {7+4,4+4},
480 {4+8,5+8}, {5+8,6+8}, {6+8,7+8}, {7+8,4+8},
481 {4+12,5+12}, {5+12,6+12}, {6+12,7+12}, {7+12,4+12},
482 {4+16,5+16}, {5+16,6+16}, {6+16,7+16}, {7+16,4+16},
483 {4+20,5+20}, {5+20,6+20}, {6+20,7+20}, {7+20,4+20},
484 {4+24,5+24}, {5+24,6+24}, {6+24,7+24}, {7+24,4+24},
487 /* Boundaries of bees */
492 void init_clip(flowstruct *sp)
496 /* Scale the planes to the screen. I had to invert the projection
497 * algorithms so that when projected they would be right at the edge of the
500 /* #### jwz: I'm not really sure what it means when sp->view.depth is 0
501 in here -- what's the right thing to do? */
503 double width = (sp->view.depth
504 ? sp->size/sp->view.depth/2
506 double height = (sp->view.depth
507 ? (sp->size/sp->view.depth/2*
508 sp->view.height/sp->view.height)
510 for (i = 0; i < PLANES; i++) {
511 /* Copy orig planes into planes, expanding <-> clippings */
512 plane[i][0][0] = plane_orig[i][0][0];
513 plane[i][0][1] = plane_orig[i][0][1] / width;
514 plane[i][0][2] = plane_orig[i][0][2] / height;
515 plane[i][1][0] = plane_orig[i][1][0];
516 plane[i][1][1] = plane_orig[i][1][1];
517 plane[i][1][2] = plane_orig[i][1][2];
519 /* Calculate the 'd' part of 'ax + by + cz = d' */
520 plane_d[i] = - plane[i][0][0] * plane[i][1][0];
521 plane_d[i] -= plane[i][0][1] * plane[i][1][1];
522 plane_d[i] -= plane[i][0][2] * plane[i][1][2];
524 xmin = X(0, i); xmax = X(0,i);
525 ymin = Y(0, i); ymax = Y(0,i);
526 zmin = Z(0, i); zmax = Z(0,i);
529 /* Scale the box defined above to fit around all points */
530 void create_box(flowstruct *sp)
533 double xmid, ymid, zmid;
534 double xsize, ysize, zsize;
537 /* Count every 5th point for speed.. */
538 for (; i < sp->beecount; i += 5) {
539 if ( X(0,i) < xmin ) xmin = X(0, i);
540 else if ( X(0,i) > xmax ) xmax = X(0, i);
541 if ( Y(0,i) < ymin ) ymin = Y(0, i);
542 else if ( Y(0,i) > ymax ) ymax = Y(0, i);
543 if ( Z(0,i) < zmin ) zmin = Z(0, i);
544 else if ( Z(0,i) > zmax ) zmax = Z(0, i);
546 xmid = (xmax+xmin)/2;
547 ymid = (ymax+ymin)/2;
548 zmid = (zmax+zmin)/2;
553 if (ysize> size) size = ysize;
554 if (zsize > size) size = zsize;
558 for (i = 0; i < BOX_P; i++) {
559 box[i][0] = box_orig[i][0] * size + xmid;
560 box[i][1] = box_orig[i][1] * size + ymid;
561 box[i][2] = box_orig[i][2] * size + zmid;
566 /* Returns true if point is infront of the plane (rather than behind) */
567 int infront_of(double x, double y, double z, int i)
569 double sum = plane[i][0][0]*x + plane[i][0][1]*y + plane[i][0][2]*z + plane_d[i];
573 /* Returns true if line was behind a clip plane, or clips the line */
574 int clip(double *x1, double *y1, double *z1, double *x2, double *y2, double *z2)
577 for (i = 0; i < PLANES; i++) {
579 double x, y, z; /* Intersection point */
580 double dx, dy, dz; /* line delta */
582 front1 = infront_of(*x1, *y1, *z1, i);
583 front2 = infront_of(*x2, *y2, *z2, i);
584 if (!front1 && !front2) return 1;
585 if (front1 && front2) continue;
591 /* Find t in line equation */
593 plane[i][0][0]*(*x1) - plane[i][0][1]*(*y1) - plane[i][0][2]*(*z1) )
595 ( plane[i][0][0]*dx + plane[i][0][1]*dy + plane[i][0][2]*dz );
600 /* Make point that was behind to be the intersect */
616 draw_flow(ModeInfo * mi)
618 Display *display = MI_DISPLAY(mi);
619 Window window = MI_WINDOW(mi);
621 flowstruct *sp = &flows[MI_SCREEN(mi)];
625 double M[3][3]; /* transformation matrix */
626 double step_view = sp->step;
627 double step_bees = sp->step;
628 double step_slow = sp->step;
633 if(!sp->view.depth){ /* simple 3D tumble */
634 double sint, cost, sinp, cosp;
635 sp->tumble.theta += sp->tumble.dtheta;
636 sp->tumble.phi += sp->tumble.dphi;
637 sint = sin(sp->tumble.theta);
638 cost = cos(sp->tumble.theta);
639 sinp = sin(sp->tumble.phi);
640 cosp = cos(sp->tumble.phi);
641 M[0][0]= cost; M[0][1]=-sint*cosp; M[0][2]= sint*sinp;
642 M[1][0]= sint; M[1][1]= cost*cosp; M[1][2]=-cost*sinp;
643 M[2][0]= 0; M[2][1]= 0; M[2][2]= 1;
644 } else { /* initialize matrix */
645 M[0][0]= 0; M[0][1]= 0; M[0][2]= 0;
646 M[1][0]= 0; M[1][1]= 0; M[1][2]= 0;
647 M[2][0]= 0; M[2][1]= 0; M[2][2]= 0;
651 for (col = 0; col < MI_NPIXELS(mi); col++)
654 MI_IS_DRAWN(mi) = True;
656 /* Calculate circling POV */
657 sp->circle[1] = sp->circle[0];
658 sp->circle[0].x = sp->size * 2 * sin(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
659 sp->circle[0].y = sp->size * 2 * cos(sp->count / 40.0) * (0.6 + 0.4 *cos(sp->count / 100.0));
660 sp->circle[0].z = sp->size * 2 * sin(sp->count / 421.0);
662 if (sp->mode & FLOW_ROTATE)
664 else if (sp->mode & FLOW_RIDE)
667 /* Bistable oscillator to switch between the trained bee and the circler */
668 pp = -sin(sin(sin(cos(sp->count / 150.0)*M_PI/2)*M_PI/2)*M_PI/2) *0.5 + 0.5;
672 /* Slow down or speed up the bees / view: */
674 /* exponentially accelerate towards zero */
675 sp->slow = sp->slow * 1.005 - 0.005;
676 if (sp->slow < 0) sp->slow = 0;
678 sp->slow_view = sp->slow_view * 1.005 - 0.005;
679 if (sp->slow_view < 0) sp->slow_view = 0;
681 /* View speeds up, slow bees slow to half speed, and other bees will
683 step_view = step_view * (1.01 - sp->slow_view * sp->slow_view) * 0.2;
684 step_slow = step_slow * (sp->slow + 0.5) / 2;
685 if (sp->mode & FLOW_SLOW)
686 step_bees = step_bees * sp->slow;
688 step_bees = step_slow;
691 for (b = 0; b < sp->beecount; b++) {
692 /* Calc if this bee is slow. Note normal bees are exempt from
693 * calculations once they slow to half speed, so that they remain as
694 * frozen lines rather than barely-visible points */
695 int slow = ((b & 0x7) == 0);
696 if ( !(sp->mode & FLOW_FREEZE) ) slow = 1;
697 /* Age the arrays. */
698 if (b < 2 || sp->slow > 0.5 || slow) {
703 /* 2nd order Kunge Kutta */
708 if (b == 0 || b == 1) {
715 k1 = sp->ODE(sp->par, X(1, b), Y(1, b), Z(1, b));
719 k2 = sp->ODE(sp->par, X(1, b) + k1.x, Y(1, b) + k1.y, Z(1, b) + k1.z);
723 X(0, b) = X(1, b) + (k1.x + k2.x) / 2.0;
724 Y(0, b) = Y(1, b) + (k1.y + k2.y) / 2.0;
725 Z(0, b) = Z(1, b) + (k1.z + k2.z) / 2.0;
730 /* Colour according to bee */
731 col = b % (MI_NPIXELS(mi) - 1);
732 ix = col * sp->beecount + sp->cnsegs[col];
734 /* Fill the segment lists. */
736 if(sp->view.depth) { /* perspective view has special points */
737 if(b==0){ /* point of view */
738 sp->centre.x = X(0, b) * pp + sp->circle[0].x * pc;
739 sp->centre.y = Y(0, b) * pp + sp->circle[0].y * pc;
740 sp->centre.z = Z(0, b) * pp + sp->circle[0].z * pc;
741 /*printf("center: (%3.3f,%3.3f,%3.3f)\n",sp->centre.x, sp->centre.y, sp->centre.z);*/
742 }else if(b==1){ /* neighbour: used to compute local axes */
743 double x[3], p[3], x2=0, xp=0, C[3][3];
747 x[0] = X(0, 0) - X(1, 0);
748 x[1] = Y(0, 0) - Y(1, 0);
749 x[2] = Z(0, 0) - Z(1, 0);
752 p[0] = X(0, 1) - X(1, 0);
753 p[1] = Y(0, 1) - Y(1, 0);
754 p[2] = Z(0, 1) - Z(1, 0);
757 x2+= x[i]*x[i]; /* X . X */
758 xp+= x[i]*p[i]; /* X . P */
759 M[0][i] = x[i]; /* X */
762 for(i=0; i<3; i++) /* (X x P) x X */
763 M[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
765 M[2][0] = x[1]*p[2] - x[2]*p[1]; /* X x P */
766 M[2][1] = -x[0]*p[2] + x[2]*p[0];
767 M[2][2] = x[0]*p[1] - x[1]*p[0];
772 for(i=0; i<3; i++) A+=M[j][i]*M[j][i]; /* sum squares */
774 for(i=0; i<3; i++) M[j][i]/=A;
777 X(0, 1)=X(0, 0)+M[1][0]; /* adjust neighbour */
778 Y(0, 1)=Y(0, 0)+M[1][1];
779 Z(0, 1)=Z(0, 0)+M[1][2];
781 /* Look at trained bee into C matrix */
783 x[0] = 0 - sp->circle[0].x;
784 x[1] = 0 - sp->circle[0].y;
785 x[2] = 0 - sp->circle[0].z;
788 p[0] = sp->circle[0].x - sp->circle[1].x;
789 p[1] = sp->circle[0].y - sp->circle[1].y;
790 p[2] = sp->circle[0].z - sp->circle[1].z;
793 x2+= x[i]*x[i]; /* X . X */
794 xp+= x[i]*p[i]; /* X . P */
795 C[0][i] = x[i]; /* X */
798 for(i=0; i<3; i++) /* (X x P) x X */
799 C[1][i] = x2*p[i] - xp*x[i]; /* == (X . X) P - (X . P) X */
801 C[2][0] = x[1]*p[2] - x[2]*p[1]; /* X x P */
802 C[2][1] = -x[0]*p[2] + x[2]*p[0];
803 C[2][2] = x[0]*p[1] - x[1]*p[0];
808 for(i=0; i<3; i++) A+=C[j][i]*C[j][i]; /* sum squares */
810 for(i=0; i<3; i++) C[j][i]/=A;
813 /* Interpolate between Center and Trained Bee matrices */
814 /* This isn't very accurate and leads to weird transformations
815 * (shearing, etc), but it works. Besides, sometimes they look
817 pp = pp * pp; /* Don't follow bee's direction until very close */
819 for (i = 0; i < 3; i++)
820 for (j = 0; j < 3; j++)
821 M[i][j] = M[i][j] * pp + C[i][j] * pc;
824 #if 0 /* display local axes for testing */
829 X(0, b)=X(0, 0)+0.5*M[0][0];
830 Y(0, b)=Y(0, 0)+0.5*M[0][1];
831 Z(0, b)=Z(0, 0)+0.5*M[0][2];
836 X(0, b)=X(0, 0)+1.5*M[2][0];
837 Y(0, b)=Y(0, 0)+1.5*M[2][1];
838 Z(0, b)=Z(0, 0)+1.5*M[2][2];
846 if (b >= MIN_BOX && b < MAX_BOX) {
847 int p1 = lines[b-MIN_BOX][0];
848 int p2 = lines[b-MIN_BOX][1];
849 X(0, b) = box[p1][0]; Y(0, b) = box[p1][1]; Z(0, b) = box[p1][2];
850 X(1, b) = box[p2][0]; Y(1, b) = box[p2][1]; Z(1, b) = box[p2][2];
854 #if 0 /* Original code */
856 double x=X(i,b)-sp->centre.x;
857 double y=Y(i,b)-sp->centre.y;
858 double z=Z(i,b)-sp->centre.z;
859 double X=M[0][0]*x + M[0][1]*y + M[0][2]*z;
860 double Y=M[1][0]*x + M[1][1]*y + M[1][2]*z;
861 double Z=M[2][0]*x + M[2][1]*y + M[2][2]*z+sp->view.height;
865 absx=SCALE_X(sp->view.depth*Y/X);
866 absy=SCALE_Y(sp->view.depth*Z/X);
867 if(absx < -sp->width || absx > 2*sp->width ||
868 absy < -sp->height || absy > 2*sp->height)
875 sp->csegs[ix].x1 = (short) absx;
876 sp->csegs[ix].y1 = (short) absy;
878 sp->csegs[ix].x2 = (short) absx;
879 sp->csegs[ix].y2 = (short) absy;
882 if(i == 2) /* both assigned */
885 /* Chalky's code w/ clipping */
886 if (b < ((sp->mode & FLOW_BOX) ? 2 : MAX_BOX))
889 double x1=X(0,b)-sp->centre.x;
890 double y1=Y(0,b)-sp->centre.y;
891 double z1=Z(0,b)-sp->centre.z;
892 double X1=M[0][0]*x1 + M[0][1]*y1 + M[0][2]*z1;
893 double Y1=M[1][0]*x1 + M[1][1]*y1 + M[1][2]*z1;
894 double Z1=M[2][0]*x1 + M[2][1]*y1 + M[2][2]*z1+sp->view.height;
896 double x2=X(1,b)-sp->centre.x;
897 double y2=Y(1,b)-sp->centre.y;
898 double z2=Z(1,b)-sp->centre.z;
899 double X2=M[0][0]*x2 + M[0][1]*y2 + M[0][2]*z2;
900 double Y2=M[1][0]*x2 + M[1][1]*y2 + M[1][2]*z2;
901 double Z2=M[2][0]*x2 + M[2][1]*y2 + M[2][2]*z2+sp->view.height;
904 /* Need clipping if: is part of box, or close to viewer */
905 if ( (b >= MIN_BOX && b < MAX_BOX) || X1 <= 0.1 || X2 < 0.1)
906 if (clip(&X1, &Y1, &Z1, &X2, &Y2, &Z2))
908 if (X1 <= 0 || X2 <= 0) break;
909 absx1=SCALE_X(sp->view.depth*Y1/X1);
910 absy1=SCALE_Y(sp->view.depth*Z1/X1);
911 if(absx1 < -sp->width || absx1 > 2*sp->width ||
912 absy1 < -sp->height || absy1 > 2*sp->height)
914 absx2=SCALE_X(sp->view.depth*Y2/X2);
915 absy2=SCALE_Y(sp->view.depth*Z2/X2);
916 if(absx2 < -sp->width || absx2 > 2*sp->width ||
917 absy2 < -sp->height || absy2 > 2*sp->height)
926 sp->csegs[ix].x1 = (short) absx1;
927 sp->csegs[ix].y1 = (short) absy1;
928 sp->csegs[ix].x2 = (short) absx2;
929 sp->csegs[ix].y2 = (short) absy2;
936 if (sp->count) { /* erase */
937 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
938 XDrawSegments(display, window, gc, sp->old_segs, sp->nold_segs);
941 if (MI_NPIXELS(mi) > 2){ /* render colour */
942 for (col = 0; col < MI_NPIXELS(mi); col++)
943 if (sp->cnsegs[col] > 0) {
944 XSetForeground(display, gc, MI_PIXEL(mi, col));
945 XDrawSegments(display, window, gc,
946 sp->csegs + col * sp->beecount, sp->cnsegs[col]);
948 } else { /* render mono */
949 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
950 XDrawSegments(display, window, gc,
951 sp->csegs + col * sp->beecount, sp->cnsegs[col]);
954 /* Copy to erase-list */
955 for (col = 0, c = 0; col < MI_NPIXELS(mi); col++)
956 for (b = 0; b < sp->cnsegs[col]; b++)
957 sp->old_segs[c++] = (sp->csegs + col * sp->beecount)[b];
960 if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
963 if (sp->count % (MI_CYCLES(mi)/4) == 0) { /* pick a new view */
964 new_view = 0; /* change to 1 .. */
967 if (X(0, 0) < xmin*2 || X(0, 0) > xmax*2) new_view = 1;
968 if (Y(0, 0) < ymin*2 || Y(0, 0) > ymax*2) new_view = 1;
969 if (Z(0, 0) < zmin*2 || Z(0, 0) > zmax*2) new_view = 1;
972 for (b = 0; b < 2; b++) {
973 X(1, b) = X(0, b) = balance_rand(sp->range.x*4);
974 Y(1, b) = Y(0, b) = balance_rand(sp->range.y*4);
975 Z(1, b) = Z(0, b) = balance_rand(sp->range.z*4);
977 sp->slow_view = 0.90;
982 release_flow(ModeInfo * mi)
987 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
988 flowstruct *sp = &flows[screen];
990 if (sp->csegs != NULL)
991 (void) free((void *) sp->csegs);
992 if (sp->cnsegs != NULL)
993 (void) free((void *) sp->cnsegs);
994 if (sp->old_segs != NULL)
995 (void) free((void *) sp->old_segs);
996 if (sp->p[0] != NULL)
997 (void) free((void *) sp->p[0]);
998 if (sp->p[1] != NULL)
999 (void) free((void *) sp->p[1]);
1001 (void) free((void *) flows);
1007 refresh_flow(ModeInfo * mi)
1012 XrmOptionDescRec flow_options[] =
1014 {"-rotate", ".rotate", XrmoptionSepArg, 0},
1015 {"-ride", ".ride", XrmoptionSepArg, 0},
1016 {"-zoom", ".zoom", XrmoptionSepArg, 0},
1017 {"-box", ".box", XrmoptionSepArg, 0},
1018 {"-slow", ".slow", XrmoptionSepArg, 0},
1019 {"-freeze", ".freeze", XrmoptionSepArg, 0},
1020 {"-allow2d", ".allow2d", XrmoptionSepArg, 0},