1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* euler2d --- 2 Dimensional Incompressible Inviscid Fluid Flow */
5 static const char sccsid[] = "@(#)euler2d.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 2000 by Stephen Montgomery-Smith <stephen@math.missouri.edu>
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
17 * This file is provided AS IS with no warranties of any kind. The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof. In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
24 * 04-Nov-2000: Added an option eulerpower. This allows for example the
25 * quasi-geostrophic equation by setting eulerpower to 2.
26 * 01-Nov-2000: Allocation checks.
27 * 10-Sep-2000: Added optimizations, and removed subtle_perturb, by stephen.
28 * 03-Sep-2000: Changed method of solving ode to Adams-Bashforth of order 2.
29 * Previously used a rather compilcated method of order 4.
30 * This doubles the speed of the program. Also it seems
31 * to have improved numerical stability. Done by stephen.
32 * 27-Aug-2000: Added rotation of region to maximize screen fill by stephen.
33 * 05-Jun-2000: Adapted from flow.c Copyright (c) 1996 by Tim Auckland
34 * 18-Jul-1996: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton.
35 * 31-Aug-1990: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org)
39 * The mathematical aspects of this program are discussed in the file
45 # define DEFAULTS "*delay: 10000 \n" \
49 "*fpsSolid: true \n" \
50 "*ignoreRotation: True \n" \
52 # define SMOOTH_COLORS
53 # define release_euler2d 0
54 # define reshape_euler2d 0
55 # define euler2d_handle_event 0
56 # include "xlockmore.h" /* in xscreensaver distribution */
57 #else /* STANDALONE */
58 # include "xlock.h" /* in xlockmore distribution */
59 #endif /* STANDALONE */
63 #define DEF_EULERTAIL "10"
65 #define DEBUG_POINTED_REGION 0
68 static int variable_boundary = 1;
69 static float power = 1;
71 static XrmOptionDescRec opts[] =
73 {"-eulertail", ".euler2d.eulertail", XrmoptionSepArg, NULL},
74 {"-eulerpower", ".euler2d.eulerpower", XrmoptionSepArg, NULL},
76 static argtype vars[] =
78 {&tail_len, "eulertail",
79 "EulerTail", (char *) DEF_EULERTAIL, t_Int},
80 {&power, "eulerpower",
81 "EulerPower", "1", t_Float},
83 static OptionStruct desc[] =
85 {"-eulertail len", "Length of Euler2d tails"},
86 {"-eulerpower power", "power of interaction law for points for Euler2d"},
89 ENTRYPOINT ModeSpecOpt euler2d_opts =
90 {sizeof opts / sizeof opts[0], opts,
91 sizeof vars / sizeof vars[0], vars, desc};
94 ModStruct euler2d_description = {
95 "euler2d", "init_euler2d", "draw_euler2d", (char *) NULL,
96 "refresh_euler2d", "init_euler2d", "free_euler2d", &euler2d_opts,
97 1000, 1024, 3000, 1, 64, 1.0, "",
98 "Simulates 2D incompressible invisid fluid.", 0, NULL
103 #define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
104 #define positive_rand(v) (LRAND()/MAXRAND*(v)) /* positive random */
106 #define number_of_vortex_points 20
108 #define n_bound_p 500
111 static double delta_t;
117 double xshift,yshift,scale;
118 double xshift2,yshift2;
124 /* x[2i+0] = x coord for nth point
125 x[2i+1] = y coord for nth point
126 w[i] = vorticity at nth point
135 /* (xs[2i+0],xs[2i+1]) is reflection of (x[2i+0],x[2i+1]) about unit circle
136 xs[2i+0] = x[2i+0]/nx
137 xs[2i+1] = x[2i+1]/nx
139 nx = x[2i+0]*x[2i+0] + x[2i+1]*x[2i+1]
141 x_is_zero[i] = (nx < 1e-10)
146 /* (p[2i+0],p[2i+1]) is image of (x[2i+0],x[2i+1]) under polynomial p.
147 mod_dp2 is |p'(z)|^2 when z = (x[2i+0],x[2i+1]).
152 /* Sometimes in our calculations we get overflow or numbers that are too big.
153 If that happens with the point x[2*i+0], x[2*i+1], we set dead[i].
166 double p_coef[2*(deg_p-1)];
171 static euler2dstruct *euler2ds = (euler2dstruct *) NULL;
174 If variable_boundary == 1, then we make a variable boundary.
175 The way this is done is to map the unit disk under a
177 p(z) = z + c_2 z^2 + ... + c_n z^n
178 where n = deg_p. sp->p_coef contains the complex numbers
182 #define add(a1,a2,b1,b2) (a1)+=(b1);(a2)+=(b2)
183 #define mult(a1,a2,b1,b2) temp=(a1)*(b1)-(a2)*(b2); \
184 (a2)=(a1)*(b2)+(a2)*(b1);(a1)=temp
187 calc_p(double *p1, double *p2, double z1, double z2, double p_coef[])
194 for(i=deg_p;i>=2;i--)
196 add(*p1,*p2,p_coef[(i-2)*2],p_coef[(i-2)*2+1]);
203 /* Calculate |p'(z)|^2 */
205 calc_mod_dp2(double z1, double z2, double p_coef[])
212 for(i=deg_p;i>=2;i--)
214 add(mp1,mp2,i*p_coef[(i-2)*2],i*p_coef[(i-2)*2+1]);
218 return mp1*mp1+mp2*mp2;
222 calc_all_p(euler2dstruct *sp)
225 double temp,p1,p2,z1,z2;
226 for(j=(sp->hide_vortex?sp->Nvortex:0);j<sp->N;j++) if(!sp->dead[j])
232 for(i=deg_p;i>=2;i--)
234 add(p1,p2,sp->p_coef[(i-2)*2],sp->p_coef[(i-2)*2+1]);
245 calc_all_mod_dp2(double *x, euler2dstruct *sp)
248 double temp,mp1,mp2,z1,z2;
249 for(j=0;j<sp->N;j++) if(!sp->dead[j])
255 for(i=deg_p;i>=2;i--)
257 add(mp1,mp2,i*sp->p_coef[(i-2)*2],i*sp->p_coef[(i-2)*2+1]);
261 sp->mod_dp2[j] = mp1*mp1+mp2*mp2;
266 derivs(double *x, euler2dstruct *sp)
269 double u1,u2,x1,x2,xij1,xij2,nxij;
272 if (variable_boundary)
273 calc_all_mod_dp2(sp->x,sp);
275 for (j=0;j<sp->Nvortex;j++) if (!sp->dead[j])
277 nx = x[2*j+0]*x[2*j+0] + x[2*j+1]*x[2*j+1];
279 sp->x_is_zero[j] = 1;
281 sp->x_is_zero[j] = 0;
282 sp->xs[2*j+0] = x[2*j+0]/nx;
283 sp->xs[2*j+1] = x[2*j+1]/nx;
287 (void) memset(sp->diffx,0,sizeof(double)*2*sp->N);
289 for (i=0;i<sp->N;i++) if (!sp->dead[i])
293 for (j=0;j<sp->Nvortex;j++) if (!sp->dead[j])
296 Calculate the Biot-Savart kernel, that is, effect of a
297 vortex point at a = (x[2*j+0],x[2*j+1]) at the point
298 x = (x1,x2), returning the vector field in (u1,u2).
300 In the plane, this is given by the formula
302 u = (x-a)/|x-a|^2 or zero if x=a.
304 However, in the unit disk we have to subtract from the
309 where as = a/|a|^2 is the reflection of a about the unit circle.
311 If however power != 1, then
313 u = (x-a)/|x-a|^(power+1) - |a|^(1-power) (x-as)/|x-as|^(power+1)
317 xij1 = x1 - x[2*j+0];
318 xij2 = x2 - x[2*j+1];
319 nxij = (power==1.0) ? xij1*xij1+xij2*xij2 : pow(xij1*xij1+xij2*xij2,(power+1)/2.0);
328 if (!sp->x_is_zero[j])
330 xij1 = x1 - sp->xs[2*j+0];
331 xij2 = x2 - sp->xs[2*j+1];
332 nxij = (power==1.0) ? xij1*xij1+xij2*xij2 : pow(xij1*xij1+xij2*xij2,(power+1)/2.0);
348 sp->diffx[2*i+0] += u1*sp->w[j];
349 sp->diffx[2*i+1] += u2*sp->w[j];
353 if (!sp->dead[i] && variable_boundary)
355 if (sp->mod_dp2[i] < 1e-5)
359 sp->diffx[2*i+0] /= sp->mod_dp2[i];
360 sp->diffx[2*i+1] /= sp->mod_dp2[i];
367 What perturb does is effectively
369 where k should be of order delta_t.
371 We have the option to do this more subtly by mapping points x
372 in the unit disk to points y in the plane, where y = f(|x|) x,
373 with f(t) = -log(1-t)/t.
375 This might reduce (but does not remove) problems where particles near
376 the edge of the boundary bounce around.
378 But it seems to be not that effective, so for now switch it off.
381 #define SUBTLE_PERTURB 0
384 perturb(double ret[], double x[], double k[], euler2dstruct *sp)
390 double d1,d2,t1,t2,mag,mag2,mlog1mmag,memmagdmag,xdotk;
391 for (i=0;i<sp->N;i++) if (!sp->dead[i])
397 mag2 = x1*x1 + x2*x2;
403 else if (mag2 > 1-1e-5)
408 mlog1mmag = -log(1-mag);
409 xdotk = x1*k1 + x2*k2;
410 t1 = (x1 + k1)*mlog1mmag/mag + x1*xdotk*(1.0/(1-mag)-mlog1mmag/mag)/mag/mag;
411 t2 = (x2 + k2)*mlog1mmag/mag + x2*xdotk*(1.0/(1-mag)-mlog1mmag/mag)/mag/mag;
412 mag = sqrt(t1*t1+t2*t2);
413 if (mag > 11.5 /* log(1e5) */)
417 memmagdmag = (mag>1e-5) ? ((1.0-exp(-mag))/mag) : (1-mag/2.0);
418 ret[2*i+0] = t1*memmagdmag;
419 ret[2*i+1] = t2*memmagdmag;
426 if (d1*d1+d2*d2 > 0.1)
433 for (i=0;i<sp->N;i++) if (!sp->dead[i])
439 if (k1*k1+k2*k2 > 0.1 || x1*x1+x2*x2 > 1-1e-5)
451 ode_solve(euler2dstruct *sp)
457 /* midpoint method */
459 (void) memcpy(sp->olddiffx,sp->diffx,sizeof(double)*2*sp->N);
460 for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
461 sp->tempdiffx[2*i+0] = 0.5*delta_t*sp->diffx[2*i+0];
462 sp->tempdiffx[2*i+1] = 0.5*delta_t*sp->diffx[2*i+1];
464 perturb(sp->tempx,sp->x,sp->tempdiffx,sp);
465 derivs(sp->tempx,sp);
466 for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
467 sp->tempdiffx[2*i+0] = delta_t*sp->diffx[2*i+0];
468 sp->tempdiffx[2*i+1] = delta_t*sp->diffx[2*i+1];
470 perturb(sp->x,sp->x,sp->tempdiffx,sp);
474 for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
475 sp->tempdiffx[2*i+0] = delta_t*(1.5*sp->diffx[2*i+0] - 0.5*sp->olddiffx[2*i+0]);
476 sp->tempdiffx[2*i+1] = delta_t*(1.5*sp->diffx[2*i+1] - 0.5*sp->olddiffx[2*i+1]);
478 perturb(sp->x,sp->x,sp->tempdiffx,sp);
480 sp->olddiffx = sp->diffx;
485 #define deallocate(p,t) if (p!=NULL) {(void) free((void *) p); p=(t*)NULL; }
486 #define allocate(p,t,s) if ((p=(t*)malloc(sizeof(t)*s))==NULL)\
487 {free_euler2d(mi);return;}
490 free_euler2d(ModeInfo * mi)
492 euler2dstruct *sp = &euler2ds[MI_SCREEN(mi)];
493 deallocate(sp->csegs, XSegment);
494 deallocate(sp->old_segs, XSegment);
495 deallocate(sp->nold_segs, int);
496 deallocate(sp->lastx, short);
497 deallocate(sp->x, double);
498 deallocate(sp->diffx, double);
499 deallocate(sp->w, double);
500 deallocate(sp->olddiffx, double);
501 deallocate(sp->tempdiffx, double);
502 deallocate(sp->tempx, double);
503 deallocate(sp->dead, short);
504 deallocate(sp->boundary, XSegment);
505 deallocate(sp->xs, double);
506 deallocate(sp->x_is_zero, short);
507 deallocate(sp->p, double);
508 deallocate(sp->mod_dp2, double);
512 init_euler2d (ModeInfo * mi)
514 #define nr_rotates 18 /* how many rotations to try to fill as much of screen as possible - must be even number */
517 double r,theta,x,y,w;
518 double mag,xscale,yscale,p1,p2;
519 double low[nr_rotates],high[nr_rotates],pp1,pp2,pn1,pn2,angle1,angle2,tempangle,dist,scale,bestscale,temp;
522 if (power<0.5) power = 0.5;
523 if (power>3.0) power = 3.0;
524 variable_boundary &= power == 1.0;
526 if (power>1.0) delta_t *= pow(0.1,power-1);
528 MI_INIT (mi, euler2ds);
529 sp = &euler2ds[MI_SCREEN(mi)];
532 jwxyz_XSetAntiAliasing (MI_DISPLAY(mi), MI_GC(mi), False);
535 sp->boundary_color = NRAND(MI_NPIXELS(mi));
536 sp->hide_vortex = NRAND(4) != 0;
540 sp->xshift2 = sp->yshift2 = 0;
542 sp->width = MI_WIDTH(mi);
543 sp->height = MI_HEIGHT(mi);
545 if (sp->width > sp->height * 5 || /* window has weird aspect */
546 sp->height > sp->width * 5)
548 if (sp->width > sp->height)
550 sp->height = sp->width * 0.8;
551 sp->yshift2 = -sp->height/2;
555 sp->width = sp->height * 0.8;
556 sp->xshift2 = -sp->width/2;
560 if (sp->width > 2560 || sp->height > 2560) /* Retina displays */
561 XSetLineAttributes (MI_DISPLAY(mi), MI_GC(mi),
562 3, LineSolid, CapRound, JoinRound);
565 sp->N = MI_COUNT(mi)+number_of_vortex_points;
566 sp->Nvortex = number_of_vortex_points;
568 if (tail_len < 1) { /* minimum tail */
571 if (tail_len > MI_CYCLES(mi)) { /* maximum tail */
572 tail_len = MI_CYCLES(mi);
575 /* Clear the background. */
578 /* Allocate memory. */
580 if (sp->csegs == NULL) {
581 allocate(sp->csegs, XSegment, sp->N);
582 allocate(sp->old_segs, XSegment, sp->N * tail_len);
583 allocate(sp->nold_segs, int, tail_len);
584 allocate(sp->lastx, short, sp->N * 2);
585 allocate(sp->x, double, sp->N * 2);
586 allocate(sp->diffx, double, sp->N * 2);
587 allocate(sp->w, double, sp->Nvortex);
588 allocate(sp->olddiffx, double, sp->N * 2);
589 allocate(sp->tempdiffx, double, sp->N * 2);
590 allocate(sp->tempx, double, sp->N * 2);
591 allocate(sp->dead, short, sp->N);
592 allocate(sp->boundary, XSegment, n_bound_p);
593 allocate(sp->xs, double, sp->Nvortex * 2);
594 allocate(sp->x_is_zero, short, sp->Nvortex);
595 allocate(sp->p, double, sp->N * 2);
596 allocate(sp->mod_dp2, double, sp->N);
598 for (i=0;i<tail_len;i++) {
599 sp->nold_segs[i] = 0;
602 (void) memset(sp->dead,0,sp->N*sizeof(short));
604 if (variable_boundary)
606 /* Initialize polynomial p */
608 The polynomial p(z) = z + c_2 z^2 + ... c_n z^n needs to be
609 a bijection of the unit disk onto its image. This is achieved
610 by insisting that sum_{k=2}^n k |c_k| <= 1. Actually we set
611 the inequality to be equality (to get more interesting shapes).
614 for(k=2;k<=deg_p;k++)
616 r = positive_rand(1.0/k);
617 theta = balance_rand(2*M_PI);
618 sp->p_coef[2*(k-2)+0]=r*cos(theta);
619 sp->p_coef[2*(k-2)+1]=r*sin(theta);
622 if (mag > 0.0001) for(k=2;k<=deg_p;k++)
624 sp->p_coef[2*(k-2)+0] /= mag;
625 sp->p_coef[2*(k-2)+1] /= mag;
628 #if DEBUG_POINTED_REGION
629 for(k=2;k<=deg_p;k++){
630 sp->p_coef[2*(k-2)+0]=0;
631 sp->p_coef[2*(k-2)+1]=0;
633 sp->p_coef[2*(6-2)+0] = 1.0/6.0;
637 /* Here we figure out the best rotation of the domain so that it fills as
638 much of the screen as possible. The number of angles we look at is determined
639 by nr_rotates (we look every 180/nr_rotates degrees).
640 While we figure out the best angle to rotate, we also figure out the correct scaling factors.
643 for(k=0;k<nr_rotates;k++) {
648 for(k=0;k<n_bound_p;k++) {
649 calc_p(&p1,&p2,cos((double)k/(n_bound_p)*2*M_PI),sin((double)k/(n_bound_p)*2*M_PI),sp->p_coef);
650 calc_p(&pp1,&pp2,cos((double)(k-1)/(n_bound_p)*2*M_PI),sin((double)(k-1)/(n_bound_p)*2*M_PI),sp->p_coef);
651 calc_p(&pn1,&pn2,cos((double)(k+1)/(n_bound_p)*2*M_PI),sin((double)(k+1)/(n_bound_p)*2*M_PI),sp->p_coef);
652 angle1 = nr_rotates/M_PI*atan2(p2-pp2,p1-pp1)-nr_rotates/2;
653 angle2 = nr_rotates/M_PI*atan2(pn2-p2,pn1-p1)-nr_rotates/2;
654 while (angle1<0) angle1+=nr_rotates*2;
655 while (angle2<0) angle2+=nr_rotates*2;
656 if (angle1>nr_rotates*1.75 && angle2<nr_rotates*0.25) angle2+=nr_rotates*2;
657 if (angle1<nr_rotates*0.25 && angle2>nr_rotates*1.75) angle1+=nr_rotates*2;
663 for(i=(int)floor(angle1);i<(int)ceil(angle2);i++) {
664 dist = cos((double)i*M_PI/nr_rotates)*p1 + sin((double)i*M_PI/nr_rotates)*p2;
665 if (i%(nr_rotates*2)<nr_rotates) {
666 if (dist>high[i%nr_rotates]) high[i%nr_rotates] = dist;
667 if (dist<low[i%nr_rotates]) low[i%nr_rotates] = dist;
670 if (-dist>high[i%nr_rotates]) high[i%nr_rotates] = -dist;
671 if (-dist<low[i%nr_rotates]) low[i%nr_rotates] = -dist;
676 for (i=0;i<nr_rotates;i++) {
677 xscale = (sp->width-5.0)/(high[i]-low[i]);
678 yscale = (sp->height-5.0)/(high[(i+nr_rotates/2)%nr_rotates]-low[(i+nr_rotates/2)%nr_rotates]);
679 scale = (xscale>yscale) ? yscale : xscale;
680 if (scale>bestscale) {
685 /* Here we do the rotation. The way we do this is to replace the
686 polynomial p(z) by a^{-1} p(a z) where a = exp(i best_angle).
690 for(k=2;k<=deg_p;k++)
692 mult(p1,p2,cos((double)besti*M_PI/nr_rotates),sin((double)besti*M_PI/nr_rotates));
693 mult(sp->p_coef[2*(k-2)+0],sp->p_coef[2*(k-2)+1],p1,p2);
696 sp->scale = bestscale;
697 sp->xshift = -(low[besti]+high[besti])/2.0*sp->scale+sp->width/2;
698 if (besti<nr_rotates/2)
699 sp->yshift = -(low[besti+nr_rotates/2]+high[besti+nr_rotates/2])/2.0*sp->scale+sp->height/2;
701 sp->yshift = (low[besti-nr_rotates/2]+high[besti-nr_rotates/2])/2.0*sp->scale+sp->height/2;
703 sp->xshift += sp->xshift2;
704 sp->yshift += sp->yshift2;
706 /* Initialize boundary */
708 for(k=0;k<n_bound_p;k++)
711 calc_p(&p1,&p2,cos((double)k/(n_bound_p)*2*M_PI),sin((double)k/(n_bound_p)*2*M_PI),sp->p_coef);
712 sp->boundary[k].x1 = (short)(p1*sp->scale+sp->xshift);
713 sp->boundary[k].y1 = (short)(p2*sp->scale+sp->yshift);
715 for(k=1;k<n_bound_p;k++)
717 sp->boundary[k].x2 = sp->boundary[k-1].x1;
718 sp->boundary[k].y2 = sp->boundary[k-1].y1;
720 sp->boundary[0].x2 = sp->boundary[n_bound_p-1].x1;
721 sp->boundary[0].y2 = sp->boundary[n_bound_p-1].y1;
725 if (sp->width>sp->height)
726 sp->radius = sp->height/2.0-5.0;
728 sp->radius = sp->width/2.0-5.0;
731 /* Initialize point positions */
733 for (i=sp->Nvortex;i<sp->N;i++) {
735 r = sqrt(positive_rand(1.0));
736 theta = balance_rand(2*M_PI);
737 sp->x[2*i+0]=r*cos(theta);
738 sp->x[2*i+1]=r*sin(theta);
739 /* This is to make sure the initial distribution of points is uniform */
740 } while (variable_boundary &&
741 calc_mod_dp2(sp->x[2*i+0],sp->x[2*i+1],sp->p_coef)
746 /* number of vortex points with negative vorticity */
751 /* if n is even make sure that np==n/2 is twice as likely
752 as the other possibilities. */
758 r = sqrt(positive_rand(0.77));
759 theta = balance_rand(2*M_PI);
762 r = 0.02+positive_rand(0.1);
763 w = (2*(k<np)-1)*2.0/sp->Nvortex;
764 for (i=sp->Nvortex*k/n;i<sp->Nvortex*(k+1)/n;i++) {
765 theta = balance_rand(2*M_PI);
766 sp->x[2*i+0]=x + r*cos(theta);
767 sp->x[2*i+1]=y + r*sin(theta);
774 draw_euler2d (ModeInfo * mi)
776 Display *display = MI_DISPLAY(mi);
777 Window window = MI_WINDOW(mi);
779 int b, col, n_non_vortex_segs;
782 MI_IS_DRAWN(mi) = True;
784 if (euler2ds == NULL)
786 sp = &euler2ds[MI_SCREEN(mi)];
787 if (sp->csegs == NULL)
791 if (variable_boundary)
795 for(b=sp->Nvortex;b<sp->N;b++) if(!sp->dead[b])
797 sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
798 sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
799 if (variable_boundary)
801 sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
802 sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
806 sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
807 sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
809 sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
810 sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
813 n_non_vortex_segs = sp->cnsegs;
815 if (!sp->hide_vortex) for(b=0;b<sp->Nvortex;b++) if(!sp->dead[b])
817 sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
818 sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
819 if (variable_boundary)
821 sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
822 sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
826 sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
827 sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
829 sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
830 sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
835 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
837 XDrawSegments(display, window, gc, sp->old_segs+sp->c_old_seg*sp->N, sp->nold_segs[sp->c_old_seg]);
839 if (MI_NPIXELS(mi) > 2){ /* render colour */
840 for (col = 0; col < MI_NPIXELS(mi); col++) {
841 int start = col*n_non_vortex_segs/MI_NPIXELS(mi);
842 int finish = (col+1)*n_non_vortex_segs/MI_NPIXELS(mi);
843 XSetForeground(display, gc, MI_PIXEL(mi, col));
844 XDrawSegments(display, window, gc,sp->csegs+start, finish-start);
846 if (!sp->hide_vortex) {
847 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
848 XDrawSegments(display, window, gc,sp->csegs+n_non_vortex_segs, sp->cnsegs-n_non_vortex_segs);
851 } else { /* render mono */
852 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
853 XDrawSegments(display, window, gc,
854 sp->csegs, sp->cnsegs);
857 if (MI_NPIXELS(mi) > 2) /* render colour */
858 XSetForeground(display, gc, MI_PIXEL(mi, sp->boundary_color));
860 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
861 if (variable_boundary)
862 XDrawSegments(display, window, gc,
863 sp->boundary, n_bound_p);
865 XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
866 sp->width/2 - (int) sp->radius - 1, sp->height/2 - (int) sp->radius -1,
867 (int) (2*sp->radius) + 2, (int) (2* sp->radius) + 2, 0, 64*360);
869 /* Copy to erase-list */
870 (void) memcpy(sp->old_segs+sp->c_old_seg*sp->N, sp->csegs, sp->cnsegs*sizeof(XSegment));
871 sp->nold_segs[sp->c_old_seg] = sp->cnsegs;
873 if (sp->c_old_seg >= tail_len)
877 if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
884 refresh_euler2d (ModeInfo * mi)
890 XSCREENSAVER_MODULE ("Euler2D", euler2d)
892 #endif /* MODE_euler2d */