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 sp->N = MI_COUNT(mi)+number_of_vortex_points;
561 sp->Nvortex = number_of_vortex_points;
563 if (tail_len < 1) { /* minimum tail */
566 if (tail_len > MI_CYCLES(mi)) { /* maximum tail */
567 tail_len = MI_CYCLES(mi);
570 /* Clear the background. */
573 /* Allocate memory. */
575 if (sp->csegs == NULL) {
576 allocate(sp->csegs, XSegment, sp->N);
577 allocate(sp->old_segs, XSegment, sp->N * tail_len);
578 allocate(sp->nold_segs, int, tail_len);
579 allocate(sp->lastx, short, sp->N * 2);
580 allocate(sp->x, double, sp->N * 2);
581 allocate(sp->diffx, double, sp->N * 2);
582 allocate(sp->w, double, sp->Nvortex);
583 allocate(sp->olddiffx, double, sp->N * 2);
584 allocate(sp->tempdiffx, double, sp->N * 2);
585 allocate(sp->tempx, double, sp->N * 2);
586 allocate(sp->dead, short, sp->N);
587 allocate(sp->boundary, XSegment, n_bound_p);
588 allocate(sp->xs, double, sp->Nvortex * 2);
589 allocate(sp->x_is_zero, short, sp->Nvortex);
590 allocate(sp->p, double, sp->N * 2);
591 allocate(sp->mod_dp2, double, sp->N);
593 for (i=0;i<tail_len;i++) {
594 sp->nold_segs[i] = 0;
597 (void) memset(sp->dead,0,sp->N*sizeof(short));
599 if (variable_boundary)
601 /* Initialize polynomial p */
603 The polynomial p(z) = z + c_2 z^2 + ... c_n z^n needs to be
604 a bijection of the unit disk onto its image. This is achieved
605 by insisting that sum_{k=2}^n k |c_k| <= 1. Actually we set
606 the inequality to be equality (to get more interesting shapes).
609 for(k=2;k<=deg_p;k++)
611 r = positive_rand(1.0/k);
612 theta = balance_rand(2*M_PI);
613 sp->p_coef[2*(k-2)+0]=r*cos(theta);
614 sp->p_coef[2*(k-2)+1]=r*sin(theta);
617 if (mag > 0.0001) for(k=2;k<=deg_p;k++)
619 sp->p_coef[2*(k-2)+0] /= mag;
620 sp->p_coef[2*(k-2)+1] /= mag;
623 #if DEBUG_POINTED_REGION
624 for(k=2;k<=deg_p;k++){
625 sp->p_coef[2*(k-2)+0]=0;
626 sp->p_coef[2*(k-2)+1]=0;
628 sp->p_coef[2*(6-2)+0] = 1.0/6.0;
632 /* Here we figure out the best rotation of the domain so that it fills as
633 much of the screen as possible. The number of angles we look at is determined
634 by nr_rotates (we look every 180/nr_rotates degrees).
635 While we figure out the best angle to rotate, we also figure out the correct scaling factors.
638 for(k=0;k<nr_rotates;k++) {
643 for(k=0;k<n_bound_p;k++) {
644 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);
645 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);
646 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);
647 angle1 = nr_rotates/M_PI*atan2(p2-pp2,p1-pp1)-nr_rotates/2;
648 angle2 = nr_rotates/M_PI*atan2(pn2-p2,pn1-p1)-nr_rotates/2;
649 while (angle1<0) angle1+=nr_rotates*2;
650 while (angle2<0) angle2+=nr_rotates*2;
651 if (angle1>nr_rotates*1.75 && angle2<nr_rotates*0.25) angle2+=nr_rotates*2;
652 if (angle1<nr_rotates*0.25 && angle2>nr_rotates*1.75) angle1+=nr_rotates*2;
658 for(i=(int)floor(angle1);i<(int)ceil(angle2);i++) {
659 dist = cos((double)i*M_PI/nr_rotates)*p1 + sin((double)i*M_PI/nr_rotates)*p2;
660 if (i%(nr_rotates*2)<nr_rotates) {
661 if (dist>high[i%nr_rotates]) high[i%nr_rotates] = dist;
662 if (dist<low[i%nr_rotates]) low[i%nr_rotates] = dist;
665 if (-dist>high[i%nr_rotates]) high[i%nr_rotates] = -dist;
666 if (-dist<low[i%nr_rotates]) low[i%nr_rotates] = -dist;
671 for (i=0;i<nr_rotates;i++) {
672 xscale = (sp->width-5.0)/(high[i]-low[i]);
673 yscale = (sp->height-5.0)/(high[(i+nr_rotates/2)%nr_rotates]-low[(i+nr_rotates/2)%nr_rotates]);
674 scale = (xscale>yscale) ? yscale : xscale;
675 if (scale>bestscale) {
680 /* Here we do the rotation. The way we do this is to replace the
681 polynomial p(z) by a^{-1} p(a z) where a = exp(i best_angle).
685 for(k=2;k<=deg_p;k++)
687 mult(p1,p2,cos((double)besti*M_PI/nr_rotates),sin((double)besti*M_PI/nr_rotates));
688 mult(sp->p_coef[2*(k-2)+0],sp->p_coef[2*(k-2)+1],p1,p2);
691 sp->scale = bestscale;
692 sp->xshift = -(low[besti]+high[besti])/2.0*sp->scale+sp->width/2;
693 if (besti<nr_rotates/2)
694 sp->yshift = -(low[besti+nr_rotates/2]+high[besti+nr_rotates/2])/2.0*sp->scale+sp->height/2;
696 sp->yshift = (low[besti-nr_rotates/2]+high[besti-nr_rotates/2])/2.0*sp->scale+sp->height/2;
698 sp->xshift += sp->xshift2;
699 sp->yshift += sp->yshift2;
701 /* Initialize boundary */
703 for(k=0;k<n_bound_p;k++)
706 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);
707 sp->boundary[k].x1 = (short)(p1*sp->scale+sp->xshift);
708 sp->boundary[k].y1 = (short)(p2*sp->scale+sp->yshift);
710 for(k=1;k<n_bound_p;k++)
712 sp->boundary[k].x2 = sp->boundary[k-1].x1;
713 sp->boundary[k].y2 = sp->boundary[k-1].y1;
715 sp->boundary[0].x2 = sp->boundary[n_bound_p-1].x1;
716 sp->boundary[0].y2 = sp->boundary[n_bound_p-1].y1;
720 if (sp->width>sp->height)
721 sp->radius = sp->height/2.0-5.0;
723 sp->radius = sp->width/2.0-5.0;
726 /* Initialize point positions */
728 for (i=sp->Nvortex;i<sp->N;i++) {
730 r = sqrt(positive_rand(1.0));
731 theta = balance_rand(2*M_PI);
732 sp->x[2*i+0]=r*cos(theta);
733 sp->x[2*i+1]=r*sin(theta);
734 /* This is to make sure the initial distribution of points is uniform */
735 } while (variable_boundary &&
736 calc_mod_dp2(sp->x[2*i+0],sp->x[2*i+1],sp->p_coef)
741 /* number of vortex points with negative vorticity */
746 /* if n is even make sure that np==n/2 is twice as likely
747 as the other possibilities. */
753 r = sqrt(positive_rand(0.77));
754 theta = balance_rand(2*M_PI);
757 r = 0.02+positive_rand(0.1);
758 w = (2*(k<np)-1)*2.0/sp->Nvortex;
759 for (i=sp->Nvortex*k/n;i<sp->Nvortex*(k+1)/n;i++) {
760 theta = balance_rand(2*M_PI);
761 sp->x[2*i+0]=x + r*cos(theta);
762 sp->x[2*i+1]=y + r*sin(theta);
769 draw_euler2d (ModeInfo * mi)
771 Display *display = MI_DISPLAY(mi);
772 Window window = MI_WINDOW(mi);
774 int b, col, n_non_vortex_segs;
777 MI_IS_DRAWN(mi) = True;
779 if (euler2ds == NULL)
781 sp = &euler2ds[MI_SCREEN(mi)];
782 if (sp->csegs == NULL)
786 if (variable_boundary)
790 for(b=sp->Nvortex;b<sp->N;b++) if(!sp->dead[b])
792 sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
793 sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
794 if (variable_boundary)
796 sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
797 sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
801 sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
802 sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
804 sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
805 sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
808 n_non_vortex_segs = sp->cnsegs;
810 if (!sp->hide_vortex) for(b=0;b<sp->Nvortex;b++) if(!sp->dead[b])
812 sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
813 sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
814 if (variable_boundary)
816 sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
817 sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
821 sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
822 sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
824 sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
825 sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
830 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
832 XDrawSegments(display, window, gc, sp->old_segs+sp->c_old_seg*sp->N, sp->nold_segs[sp->c_old_seg]);
834 if (MI_NPIXELS(mi) > 2){ /* render colour */
835 for (col = 0; col < MI_NPIXELS(mi); col++) {
836 int start = col*n_non_vortex_segs/MI_NPIXELS(mi);
837 int finish = (col+1)*n_non_vortex_segs/MI_NPIXELS(mi);
838 XSetForeground(display, gc, MI_PIXEL(mi, col));
839 XDrawSegments(display, window, gc,sp->csegs+start, finish-start);
841 if (!sp->hide_vortex) {
842 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
843 XDrawSegments(display, window, gc,sp->csegs+n_non_vortex_segs, sp->cnsegs-n_non_vortex_segs);
846 } else { /* render mono */
847 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
848 XDrawSegments(display, window, gc,
849 sp->csegs, sp->cnsegs);
852 if (MI_NPIXELS(mi) > 2) /* render colour */
853 XSetForeground(display, gc, MI_PIXEL(mi, sp->boundary_color));
855 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
856 if (variable_boundary)
857 XDrawSegments(display, window, gc,
858 sp->boundary, n_bound_p);
860 XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
861 sp->width/2 - (int) sp->radius - 1, sp->height/2 - (int) sp->radius -1,
862 (int) (2*sp->radius) + 2, (int) (2* sp->radius) + 2, 0, 64*360);
864 /* Copy to erase-list */
865 (void) memcpy(sp->old_segs+sp->c_old_seg*sp->N, sp->csegs, sp->cnsegs*sizeof(XSegment));
866 sp->nold_segs[sp->c_old_seg] = sp->cnsegs;
868 if (sp->c_old_seg >= tail_len)
872 if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
879 refresh_euler2d (ModeInfo * mi)
885 XSCREENSAVER_MODULE ("Euler2D", euler2d)
887 #endif /* MODE_euler2d */