source: trunk/third/xscreensaver/hacks/euler2d.c @ 20148

Revision 20148, 23.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 4 -*- */
2/* euler2d --- 2 Dimensional Incompressible Inviscid Fluid Flow */
3
4#if 0
5static const char sccsid[] = "@(#)euler2d.c     5.00 2000/11/01 xlockmore";
6#endif
7
8/*
9 * Copyright (c) 2000 by Stephen Montgomery-Smith <stephen@math.missouri.edu>
10 *
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.
16 *
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.
22 *
23 * Revision History:
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)
36 */
37
38/*
39 * The mathematical aspects of this program are discussed in the file
40 * euler2d.tex.
41 */
42
43#ifdef STANDALONE
44#define MODE_euler2d
45#define PROGCLASS "Euler2d"
46#define HACK_INIT init_euler2d
47#define HACK_DRAW draw_euler2d
48#define euler2d_opts xlockmore_opts
49#define DEFAULTS "*delay: 10000 \n" \
50"*count: 1024 \n" \
51"*cycles: 3000 \n" \
52"*ncolors: 64 \n"
53#define SMOOTH_COLORS
54#include "xlockmore.h"          /* in xscreensaver distribution */
55#else /* STANDALONE */
56#include "xlock.h"              /* in xlockmore distribution */
57#endif /* STANDALONE */
58
59#ifdef MODE_euler2d
60
61#define DEF_EULERTAIL "10"
62
63#define DEBUG_POINTED_REGION    0
64
65static int tail_len;
66static int variable_boundary = 1;
67static float power = 1;
68
69static XrmOptionDescRec opts[] =
70{
71  {(char* ) "-eulertail", (char *) ".euler2d.eulertail",
72   XrmoptionSepArg, (caddr_t) NULL},
73  {(char* ) "-eulerpower", (char *) ".euler2d.eulerpower",
74   XrmoptionSepArg, (caddr_t) NULL},
75};
76static argtype vars[] =
77{
78  {(caddr_t *) &tail_len, (char *) "eulertail",
79   (char *) "EulerTail", (char *) DEF_EULERTAIL, t_Int},
80  {(caddr_t *) &power, (char *) "eulerpower",
81   (char *) "EulerPower", (char *) "1", t_Float},
82};
83static OptionStruct desc[] =
84{
85  {(char *) "-eulertail len", (char *) "Length of Euler2d tails"},
86  {(char *) "-eulerpower power", (char *) "power of interaction law for points for Euler2d"},
87};
88
89ModeSpecOpt euler2d_opts =
90{sizeof opts / sizeof opts[0], opts,
91 sizeof vars / sizeof vars[0], vars, desc};
92
93#ifdef USE_MODULES
94ModStruct   euler2d_description = {
95        "euler2d", "init_euler2d", "draw_euler2d", "release_euler2d",
96        "refresh_euler2d", "init_euler2d", (char *) NULL, &euler2d_opts,
97        1000, 1024, 3000, 1, 64, 1.0, "",
98        "Simulates 2D incompressible invisid fluid.", 0, NULL
99};
100
101#endif
102
103#define balance_rand(v) ((LRAND()/MAXRAND*(v))-((v)/2)) /* random around 0 */
104#define positive_rand(v)        (LRAND()/MAXRAND*(v))   /* positive random */
105
106#define number_of_vortex_points 20
107
108#define n_bound_p 500
109#define deg_p 6
110
111static double delta_t;
112
113typedef struct {
114        int        width;
115        int        height;
116        int        count;
117        double     xshift,yshift,scale;
118        double     radius;
119
120        int        N;
121        int        Nvortex;
122
123/*  x[2i+0] = x coord for nth point
124    x[2i+1] = y coord for nth point
125    w[i] = vorticity at nth point
126*/
127        double     *x;
128        double     *w;
129
130        double     *diffx;
131        double     *olddiffx;
132        double     *tempx;
133        double     *tempdiffx;
134/*  (xs[2i+0],xs[2i+1]) is reflection of (x[2i+0],x[2i+1]) about unit circle
135    xs[2i+0] = x[2i+0]/nx
136    xs[2i+1] = x[2i+1]/nx
137    where
138    nx = x[2i+0]*x[2i+0] + x[2i+1]*x[2i+1]
139
140    x_is_zero[i] = (nx < 1e-10)
141*/
142        double     *xs;
143        short      *x_is_zero;
144
145/*  (p[2i+0],p[2i+1]) is image of (x[2i+0],x[2i+1]) under polynomial p.
146    mod_dp2 is |p'(z)|^2 when z = (x[2i+0],x[2i+1]).
147*/
148        double     *p;
149        double     *mod_dp2;
150
151/* Sometimes in our calculations we get overflow or numbers that are too big. 
152   If that happens with the point x[2*i+0], x[2*i+1], we set dead[i].
153*/
154        short      *dead;
155
156        XSegment   *csegs;
157        int         cnsegs;
158        XSegment   *old_segs;
159        int        *nold_segs;
160        int         c_old_seg;
161        int         boundary_color;
162        int         hide_vortex;
163        short      *lastx;
164
165        double      p_coef[2*(deg_p-1)];
166        XSegment   *boundary;
167
168} euler2dstruct;
169
170static euler2dstruct *euler2ds = (euler2dstruct *) NULL;
171
172/*
173  If variable_boundary == 1, then we make a variable boundary.
174  The way this is done is to map the unit disk under a
175  polynomial p, where
176  p(z) = z + c_2 z^2 + ... + c_n z^n
177  where n = deg_p.  sp->p_coef contains the complex numbers
178  c_2, c_3, ... c_n.
179*/
180
181#define add(a1,a2,b1,b2) (a1)+=(b1);(a2)+=(b2)
182#define mult(a1,a2,b1,b2) temp=(a1)*(b1)-(a2)*(b2); \
183                          (a2)=(a1)*(b2)+(a2)*(b1);(a1)=temp
184
185static void
186calc_p(double *p1, double *p2, double z1, double z2, double p_coef[])
187{
188  int i;
189  double temp;
190
191  *p1=0;
192  *p2=0;
193  for(i=deg_p;i>=2;i--)
194  {
195    add(*p1,*p2,p_coef[(i-2)*2],p_coef[(i-2)*2+1]);
196    mult(*p1,*p2,z1,z2);
197  }
198  add(*p1,*p2,1,0);
199  mult(*p1,*p2,z1,z2);
200}
201
202/* Calculate |p'(z)|^2 */
203static double
204calc_mod_dp2(double z1, double z2, double p_coef[])
205{
206  int i;
207  double temp,mp1,mp2;
208
209  mp1=0;
210  mp2=0;
211  for(i=deg_p;i>=2;i--)
212  {
213    add(mp1,mp2,i*p_coef[(i-2)*2],i*p_coef[(i-2)*2+1]);
214    mult(mp1,mp2,z1,z2);
215  }
216  add(mp1,mp2,1,0);
217  return mp1*mp1+mp2*mp2;
218}
219
220static void
221calc_all_p(euler2dstruct *sp)
222{
223  int i,j;
224  double temp,p1,p2,z1,z2;
225  for(j=(sp->hide_vortex?sp->Nvortex:0);j<sp->N;j++) if(!sp->dead[j])
226  {
227    p1=0;
228    p2=0;
229    z1=sp->x[2*j+0];
230    z2=sp->x[2*j+1];
231    for(i=deg_p;i>=2;i--)
232    {
233      add(p1,p2,sp->p_coef[(i-2)*2],sp->p_coef[(i-2)*2+1]);
234      mult(p1,p2,z1,z2);
235    }
236    add(p1,p2,1,0);
237    mult(p1,p2,z1,z2);
238    sp->p[2*j+0] = p1;
239    sp->p[2*j+1] = p2;
240  }
241}
242
243static void
244calc_all_mod_dp2(double *x, euler2dstruct *sp)
245{
246  int i,j;
247  double temp,mp1,mp2,z1,z2;
248  for(j=0;j<sp->N;j++) if(!sp->dead[j])
249  {
250    mp1=0;
251    mp2=0;
252    z1=x[2*j+0];
253    z2=x[2*j+1];
254    for(i=deg_p;i>=2;i--)
255    {
256      add(mp1,mp2,i*sp->p_coef[(i-2)*2],i*sp->p_coef[(i-2)*2+1]);
257      mult(mp1,mp2,z1,z2);
258    }
259    add(mp1,mp2,1,0);
260    sp->mod_dp2[j] = mp1*mp1+mp2*mp2;
261  }
262}
263
264static void
265derivs(double *x, euler2dstruct *sp)
266{
267  int i,j;
268  double u1,u2,x1,x2,xij1,xij2,nxij;
269  double nx;
270
271  if (variable_boundary)
272    calc_all_mod_dp2(sp->x,sp);
273
274  for (j=0;j<sp->Nvortex;j++) if (!sp->dead[j])
275  {
276    nx = x[2*j+0]*x[2*j+0] + x[2*j+1]*x[2*j+1];
277    if (nx < 1e-10)
278      sp->x_is_zero[j] = 1;
279    else {
280      sp->x_is_zero[j] = 0;
281      sp->xs[2*j+0] = x[2*j+0]/nx;
282      sp->xs[2*j+1] = x[2*j+1]/nx;
283    }
284  }
285
286  (void) memset(sp->diffx,0,sizeof(double)*2*sp->N);
287
288  for (i=0;i<sp->N;i++) if (!sp->dead[i])
289  {
290    x1 = x[2*i+0];
291    x2 = x[2*i+1];
292    for (j=0;j<sp->Nvortex;j++) if (!sp->dead[j])
293    {
294/*
295  Calculate the Biot-Savart kernel, that is, effect of a
296  vortex point at a = (x[2*j+0],x[2*j+1]) at the point
297  x = (x1,x2), returning the vector field in (u1,u2).
298
299  In the plane, this is given by the formula
300
301  u = (x-a)/|x-a|^2  or zero if x=a.
302
303  However, in the unit disk we have to subtract from the
304  above:
305
306  (x-as)/|x-as|^2
307
308  where as = a/|a|^2 is the reflection of a about the unit circle.
309
310  If however power != 1, then
311
312  u = (x-a)/|x-a|^(power+1)  -  |a|^(1-power) (x-as)/|x-as|^(power+1)
313
314*/
315
316      xij1 = x1 - x[2*j+0];
317      xij2 = x2 - x[2*j+1];
318      nxij = (power==1.0) ? xij1*xij1+xij2*xij2 : pow(xij1*xij1+xij2*xij2,(power+1)/2.0);
319
320      if(nxij >= 1e-4)  {
321        u1 =  xij2/nxij;
322        u2 = -xij1/nxij;
323      }
324      else
325        u1 = u2 = 0.0;
326
327      if (!sp->x_is_zero[j])
328      {
329        xij1 = x1 - sp->xs[2*j+0];
330        xij2 = x2 - sp->xs[2*j+1];
331        nxij = (power==1.0) ? xij1*xij1+xij2*xij2 : pow(xij1*xij1+xij2*xij2,(power+1)/2.0);
332
333        if (nxij < 1e-5)
334        {
335          sp->dead[i] = 1;
336          u1 = u2 = 0.0;
337        }
338        else
339        {
340          u1 -= xij2/nxij;
341          u2 += xij1/nxij;
342        }
343      }
344
345      if (!sp->dead[i])
346      {
347        sp->diffx[2*i+0] += u1*sp->w[j];
348        sp->diffx[2*i+1] += u2*sp->w[j];
349      }
350    }
351
352    if (!sp->dead[i] && variable_boundary)
353    {
354      if (sp->mod_dp2[i] < 1e-5)
355        sp->dead[i] = 1;
356      else
357      {
358        sp->diffx[2*i+0] /= sp->mod_dp2[i];
359        sp->diffx[2*i+1] /= sp->mod_dp2[i];
360      }
361    }
362  }
363}
364
365/*
366  What perturb does is effectively
367    ret = x + k,
368  where k should be of order delta_t. 
369
370  We have the option to do this more subtly by mapping points x
371  in the unit disk to points y in the plane, where y = f(|x|) x,
372  with f(t) = -log(1-t)/t.
373
374  This might reduce (but does not remove) problems where particles near
375  the edge of the boundary bounce around.
376
377  But it seems to be not that effective, so for now switch it off.
378*/
379
380#define SUBTLE_PERTURB 0
381
382static void
383perturb(double ret[], double x[], double k[], euler2dstruct *sp)
384{
385  int i;
386  double x1,x2,k1,k2;
387
388#if SUBTLE_PERTURB
389  double d1,d2,t1,t2,mag,mag2,mlog1mmag,memmagdmag,xdotk;
390  for (i=0;i<sp->N;i++) if (!sp->dead[i])
391  {
392    x1 = x[2*i+0];
393    x2 = x[2*i+1];
394    k1 = k[2*i+0];
395    k2 = k[2*i+1];
396    mag2 = x1*x1 + x2*x2;
397    if (mag2 < 1e-10)
398    {
399      ret[2*i+0] = x1+k1;
400      ret[2*i+1] = x2+k2;
401    }
402    else if (mag2 > 1-1e-5)
403      sp->dead[i] = 1;
404    else
405    {
406      mag = sqrt(mag2);
407      mlog1mmag = -log(1-mag);
408      xdotk = x1*k1 + x2*k2;
409      t1 = (x1 + k1)*mlog1mmag/mag + x1*xdotk*(1.0/(1-mag)-mlog1mmag/mag)/mag/mag;
410      t2 = (x2 + k2)*mlog1mmag/mag + x2*xdotk*(1.0/(1-mag)-mlog1mmag/mag)/mag/mag;
411      mag = sqrt(t1*t1+t2*t2);
412      if (mag > 11.5 /* log(1e5) */)
413        sp->dead[i] = 1;
414      else
415      {
416        memmagdmag = (mag>1e-5) ? ((1.0-exp(-mag))/mag) : (1-mag/2.0);
417        ret[2*i+0] = t1*memmagdmag;
418        ret[2*i+1] = t2*memmagdmag;
419      }
420    }
421    if (!sp->dead[i])
422    {
423      d1 = ret[2*i+0]-x1;
424      d2 = ret[2*i+1]-x2;
425      if (d1*d1+d2*d2 > 0.1)
426        sp->dead[i] = 1;
427    }
428  }
429
430#else
431
432  for (i=0;i<sp->N;i++) if (!sp->dead[i])
433  {
434    x1 = x[2*i+0];
435    x2 = x[2*i+1];
436    k1 = k[2*i+0];
437    k2 = k[2*i+1];
438    if (k1*k1+k2*k2 > 0.1 || x1*x1+x2*x2 > 1-1e-5)
439      sp->dead[i] = 1;
440    else
441    {
442      ret[2*i+0] = x1+k1;
443      ret[2*i+1] = x2+k2;
444    }
445  }
446#endif
447}
448
449static void
450ode_solve(euler2dstruct *sp)
451{
452  int i;
453  double *temp;
454
455  if (sp->count < 1) {
456    /* midpoint method */
457    derivs(sp->x,sp);
458    (void) memcpy(sp->olddiffx,sp->diffx,sizeof(double)*2*sp->N);
459    for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
460      sp->tempdiffx[2*i+0] = 0.5*delta_t*sp->diffx[2*i+0];
461      sp->tempdiffx[2*i+1] = 0.5*delta_t*sp->diffx[2*i+1];
462    }
463    perturb(sp->tempx,sp->x,sp->tempdiffx,sp);
464    derivs(sp->tempx,sp);
465    for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
466      sp->tempdiffx[2*i+0] = delta_t*sp->diffx[2*i+0];
467      sp->tempdiffx[2*i+1] = delta_t*sp->diffx[2*i+1];
468    }
469    perturb(sp->x,sp->x,sp->tempdiffx,sp);
470  } else {
471    /* Adams Basforth */
472    derivs(sp->x,sp);
473    for (i=0;i<sp->N;i++) if (!sp->dead[i]) {
474      sp->tempdiffx[2*i+0] = delta_t*(1.5*sp->diffx[2*i+0] - 0.5*sp->olddiffx[2*i+0]);
475      sp->tempdiffx[2*i+1] = delta_t*(1.5*sp->diffx[2*i+1] - 0.5*sp->olddiffx[2*i+1]);
476    }
477    perturb(sp->x,sp->x,sp->tempdiffx,sp);
478    temp = sp->olddiffx;
479    sp->olddiffx = sp->diffx;
480    sp->diffx = temp;
481  }
482}
483
484#define deallocate(p,t) if (p!=NULL) {(void) free((void *) p); p=(t*)NULL; }
485#define allocate(p,t,s) if ((p=(t*)malloc(sizeof(t)*s))==NULL)\
486{free_euler2d(sp);return;}
487
488static void
489free_euler2d(euler2dstruct *sp)
490{
491        deallocate(sp->csegs, XSegment);
492        deallocate(sp->old_segs, XSegment);
493        deallocate(sp->nold_segs, int);
494        deallocate(sp->lastx, short);
495        deallocate(sp->x, double);
496        deallocate(sp->diffx, double);
497        deallocate(sp->w, double);
498        deallocate(sp->olddiffx, double);
499        deallocate(sp->tempdiffx, double);
500        deallocate(sp->tempx, double);
501        deallocate(sp->dead, short);
502        deallocate(sp->boundary, XSegment);
503        deallocate(sp->xs, double);
504        deallocate(sp->x_is_zero, short);
505        deallocate(sp->p, double);
506        deallocate(sp->mod_dp2, double);
507}
508
509void
510init_euler2d(ModeInfo * mi)
511{
512#define nr_rotates 18 /* how many rotations to try to fill as much of screen as possible - must be even number */
513        euler2dstruct *sp;
514        int         i,k,n,np;
515        double      r,theta,x,y,w;
516        double      mag,xscale,yscale,p1,p2;
517        double      low[nr_rotates],high[nr_rotates],pp1,pp2,pn1,pn2,angle1,angle2,tempangle,dist,scale,bestscale,temp;
518        int         besti = 0;
519
520        if (power<0.5) power = 0.5;
521        if (power>3.0) power = 3.0;
522        variable_boundary &= power == 1.0;
523        delta_t = 0.001;
524        if (power>1.0) delta_t *= pow(0.1,power-1);
525
526        if (euler2ds == NULL) {
527                if ((euler2ds = (euler2dstruct *) calloc(MI_NUM_SCREENS(mi),
528                                               sizeof (euler2dstruct))) == NULL)
529                        return;
530        }
531        sp = &euler2ds[MI_SCREEN(mi)];
532
533        sp->boundary_color = NRAND(MI_NPIXELS(mi));
534        sp->hide_vortex = NRAND(4) != 0;
535
536        sp->count = 0;
537
538        sp->width = MI_WIDTH(mi);
539        sp->height = MI_HEIGHT(mi);
540
541        sp->N = MI_COUNT(mi)+number_of_vortex_points;
542        sp->Nvortex = number_of_vortex_points;
543
544        if (tail_len < 1) { /* minimum tail */
545          tail_len = 1;
546        }
547        if (tail_len > MI_CYCLES(mi)) { /* maximum tail */
548          tail_len = MI_CYCLES(mi);
549        }
550
551        /* Clear the background. */
552        MI_CLEARWINDOW(mi);
553         
554        free_euler2d(sp);
555
556        /* Allocate memory. */
557
558        if (sp->csegs == NULL) {
559                allocate(sp->csegs, XSegment, sp->N);
560                allocate(sp->old_segs, XSegment, sp->N * tail_len);
561                allocate(sp->nold_segs, int, tail_len);
562                allocate(sp->lastx, short, sp->N * 2);
563                allocate(sp->x, double, sp->N * 2);
564                allocate(sp->diffx, double, sp->N * 2);
565                allocate(sp->w, double, sp->Nvortex);
566                allocate(sp->olddiffx, double, sp->N * 2);
567                allocate(sp->tempdiffx, double, sp->N * 2);
568                allocate(sp->tempx, double, sp->N * 2);
569                allocate(sp->dead, short, sp->N);
570                allocate(sp->boundary, XSegment, n_bound_p);
571                allocate(sp->xs, double, sp->Nvortex * 2);
572                allocate(sp->x_is_zero, short, sp->Nvortex);
573                allocate(sp->p, double, sp->N * 2);
574                allocate(sp->mod_dp2, double, sp->N);
575        }
576        for (i=0;i<tail_len;i++) {
577                sp->nold_segs[i] = 0;
578        }
579        sp->c_old_seg = 0;
580        (void) memset(sp->dead,0,sp->N*sizeof(short));
581
582        if (variable_boundary)
583        {
584        /* Initialize polynomial p */
585/*
586  The polynomial p(z) = z + c_2 z^2 + ... c_n z^n needs to be
587  a bijection of the unit disk onto its image.  This is achieved
588  by insisting that sum_{k=2}^n k |c_k| <= 1.  Actually we set
589  the inequality to be equality (to get more interesting shapes).
590*/
591                mag = 0;
592                for(k=2;k<=deg_p;k++)
593                {
594                        r = positive_rand(1.0/k);
595                        theta = balance_rand(2*M_PI);
596                        sp->p_coef[2*(k-2)+0]=r*cos(theta);
597                        sp->p_coef[2*(k-2)+1]=r*sin(theta);
598                        mag += k*r;
599                }
600                if (mag > 0.0001) for(k=2;k<=deg_p;k++)
601                {
602                        sp->p_coef[2*(k-2)+0] /= mag;
603                        sp->p_coef[2*(k-2)+1] /= mag;
604                }
605
606#if DEBUG_POINTED_REGION
607                for(k=2;k<=deg_p;k++){
608                        sp->p_coef[2*(k-2)+0]=0;
609                        sp->p_coef[2*(k-2)+1]=0;
610                }
611                sp->p_coef[2*(6-2)+0] = 1.0/6.0;
612#endif
613
614
615/* Here we figure out the best rotation of the domain so that it fills as
616   much of the screen as possible.  The number of angles we look at is determined
617   by nr_rotates (we look every 180/nr_rotates degrees). 
618   While we figure out the best angle to rotate, we also figure out the correct scaling factors.
619*/
620
621                for(k=0;k<nr_rotates;k++) {
622                        low[k] = 1e5;
623                        high[k] = -1e5;
624                }
625
626                for(k=0;k<n_bound_p;k++) {
627                        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);
628                        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);
629                        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);
630                        angle1 = nr_rotates/M_PI*atan2(p2-pp2,p1-pp1)-nr_rotates/2;
631                        angle2 = nr_rotates/M_PI*atan2(pn2-p2,pn1-p1)-nr_rotates/2;
632                        while (angle1<0) angle1+=nr_rotates*2;
633                        while (angle2<0) angle2+=nr_rotates*2;
634                        if (angle1>nr_rotates*1.75 && angle2<nr_rotates*0.25) angle2+=nr_rotates*2;
635                        if (angle1<nr_rotates*0.25 && angle2>nr_rotates*1.75) angle1+=nr_rotates*2;
636                        if (angle2<angle1) {
637                                tempangle=angle1;
638                                angle1=angle2;
639                                angle2=tempangle;
640                        }
641                        for(i=(int)floor(angle1);i<(int)ceil(angle2);i++) {
642                                dist = cos((double)i*M_PI/nr_rotates)*p1 + sin((double)i*M_PI/nr_rotates)*p2;
643                                if (i%(nr_rotates*2)<nr_rotates) {
644                                        if (dist>high[i%nr_rotates]) high[i%nr_rotates] = dist;
645                                        if (dist<low[i%nr_rotates]) low[i%nr_rotates] = dist;
646                                }
647                                else {
648                                        if (-dist>high[i%nr_rotates]) high[i%nr_rotates] = -dist;
649                                        if (-dist<low[i%nr_rotates]) low[i%nr_rotates] = -dist;
650                                }
651                        }
652                }
653                bestscale = 0;
654                for (i=0;i<nr_rotates;i++) {
655                        xscale = (sp->width-5.0)/(high[i]-low[i]);
656                        yscale = (sp->height-5.0)/(high[(i+nr_rotates/2)%nr_rotates]-low[(i+nr_rotates/2)%nr_rotates]);
657                        scale = (xscale>yscale) ? yscale : xscale;
658                        if (scale>bestscale) {
659                                bestscale = scale;
660                                besti = i;
661                        }
662                }
663/* Here we do the rotation.  The way we do this is to replace the
664   polynomial p(z) by a^{-1} p(a z) where a = exp(i best_angle).
665*/
666                p1 = 1;
667                p2 = 0;
668                for(k=2;k<=deg_p;k++)
669                {
670                        mult(p1,p2,cos((double)besti*M_PI/nr_rotates),sin((double)besti*M_PI/nr_rotates));
671                        mult(sp->p_coef[2*(k-2)+0],sp->p_coef[2*(k-2)+1],p1,p2);
672                }
673
674                sp->scale = bestscale;
675                sp->xshift = -(low[besti]+high[besti])/2.0*sp->scale+sp->width/2;
676                if (besti<nr_rotates/2)
677                        sp->yshift = -(low[besti+nr_rotates/2]+high[besti+nr_rotates/2])/2.0*sp->scale+sp->height/2;
678                else
679                        sp->yshift = (low[besti-nr_rotates/2]+high[besti-nr_rotates/2])/2.0*sp->scale+sp->height/2;
680
681
682/* Initialize boundary */
683
684                for(k=0;k<n_bound_p;k++)
685                {
686
687                        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);
688                        sp->boundary[k].x1 = (short)(p1*sp->scale+sp->xshift);
689                        sp->boundary[k].y1 = (short)(p2*sp->scale+sp->yshift);
690                }
691                for(k=1;k<n_bound_p;k++)
692                {
693                        sp->boundary[k].x2 = sp->boundary[k-1].x1;
694                        sp->boundary[k].y2 = sp->boundary[k-1].y1;
695                }
696                sp->boundary[0].x2 = sp->boundary[n_bound_p-1].x1;
697                sp->boundary[0].y2 = sp->boundary[n_bound_p-1].y1;
698        }
699        else
700        {
701                if (sp->width>sp->height)
702                        sp->radius = sp->height/2.0-5.0;
703                else
704                        sp->radius = sp->width/2.0-5.0;
705        }
706
707        /* Initialize point positions */
708
709        for (i=sp->Nvortex;i<sp->N;i++) {
710                do {
711                        r = sqrt(positive_rand(1.0));
712                        theta = balance_rand(2*M_PI);
713                        sp->x[2*i+0]=r*cos(theta);
714                        sp->x[2*i+1]=r*sin(theta);
715                /* This is to make sure the initial distribution of points is uniform */
716                } while (variable_boundary &&
717                          calc_mod_dp2(sp->x[2*i+0],sp->x[2*i+1],sp->p_coef)
718                          < positive_rand(4));
719        }
720
721        n = NRAND(4)+2;
722        /* number of vortex points with negative vorticity */
723        if (n%2) {
724                np = NRAND(n+1);
725        }
726        else {
727                /* if n is even make sure that np==n/2 is twice as likely
728                   as the other possibilities. */
729                np = NRAND(n+2);
730                if (np==n+1) np=n/2;
731        }
732        for(k=0;k<n;k++)
733        {
734                r = sqrt(positive_rand(0.77));
735                theta = balance_rand(2*M_PI);
736                x=r*cos(theta);
737                y=r*sin(theta);
738                r = 0.02+positive_rand(0.1);
739                w = (2*(k<np)-1)*2.0/sp->Nvortex;
740                for (i=sp->Nvortex*k/n;i<sp->Nvortex*(k+1)/n;i++) {
741                        theta = balance_rand(2*M_PI);
742                        sp->x[2*i+0]=x + r*cos(theta);
743                        sp->x[2*i+1]=y + r*sin(theta);
744                        sp->w[i]=w;
745                }
746        }
747}
748
749void
750draw_euler2d(ModeInfo * mi)
751{
752        Display    *display = MI_DISPLAY(mi);
753        Window      window = MI_WINDOW(mi);
754        GC          gc = MI_GC(mi);
755        int         b, col, n_non_vortex_segs;
756        euler2dstruct *sp;
757
758        MI_IS_DRAWN(mi) = True;
759
760        if (euler2ds == NULL)
761                return;
762        sp = &euler2ds[MI_SCREEN(mi)];
763        if (sp->csegs == NULL)
764                return;
765
766        ode_solve(sp);
767        if (variable_boundary)
768                calc_all_p(sp);
769
770        sp->cnsegs = 0;
771        for(b=sp->Nvortex;b<sp->N;b++) if(!sp->dead[b])
772        {
773                sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
774                sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
775                if (variable_boundary)
776                {
777                        sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
778                        sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
779                }
780                else
781                {
782                        sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
783                        sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
784                }
785                sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
786                sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
787                sp->cnsegs++;
788        }
789        n_non_vortex_segs = sp->cnsegs;
790
791        if (!sp->hide_vortex) for(b=0;b<sp->Nvortex;b++) if(!sp->dead[b])
792        {
793                sp->csegs[sp->cnsegs].x1 = sp->lastx[2*b+0];
794                sp->csegs[sp->cnsegs].y1 = sp->lastx[2*b+1];
795                if (variable_boundary)
796                {
797                        sp->csegs[sp->cnsegs].x2 = (short)(sp->p[2*b+0]*sp->scale+sp->xshift);
798                        sp->csegs[sp->cnsegs].y2 = (short)(sp->p[2*b+1]*sp->scale+sp->yshift);
799                }
800                else
801                {
802                        sp->csegs[sp->cnsegs].x2 = (short)(sp->x[2*b+0]*sp->radius+sp->width/2);
803                        sp->csegs[sp->cnsegs].y2 = (short)(sp->x[2*b+1]*sp->radius+sp->height/2);
804                }
805                sp->lastx[2*b+0] = sp->csegs[sp->cnsegs].x2;
806                sp->lastx[2*b+1] = sp->csegs[sp->cnsegs].y2;
807                sp->cnsegs++;
808        }
809
810        if (sp->count) {
811                XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
812
813                XDrawSegments(display, window, gc, sp->old_segs+sp->c_old_seg*sp->N, sp->nold_segs[sp->c_old_seg]);
814
815                if (MI_NPIXELS(mi) > 2){ /* render colour */
816                        for (col = 0; col < MI_NPIXELS(mi); col++) {
817                          int start = col*n_non_vortex_segs/MI_NPIXELS(mi);
818                          int finish = (col+1)*n_non_vortex_segs/MI_NPIXELS(mi);
819                          XSetForeground(display, gc, MI_PIXEL(mi, col));
820                          XDrawSegments(display, window, gc,sp->csegs+start, finish-start);
821                        }
822                        if (!sp->hide_vortex) {
823                          XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
824                          XDrawSegments(display, window, gc,sp->csegs+n_non_vortex_segs, sp->cnsegs-n_non_vortex_segs);
825                        }
826
827                } else {                /* render mono */
828                  XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
829                  XDrawSegments(display, window, gc,
830                                                sp->csegs, sp->cnsegs);
831                }
832
833                if (MI_NPIXELS(mi) > 2) /* render colour */
834                        XSetForeground(display, gc, MI_PIXEL(mi, sp->boundary_color));
835                else
836                        XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
837                if (variable_boundary)
838                        XDrawSegments(display, window, gc,
839                                                sp->boundary, n_bound_p);
840                else
841                        XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
842                                 sp->width/2 - (int) sp->radius - 1, sp->height/2 - (int) sp->radius -1,
843                                 (int) (2*sp->radius) + 2, (int) (2* sp->radius) + 2, 0, 64*360);
844
845                /* Copy to erase-list */
846                (void) memcpy(sp->old_segs+sp->c_old_seg*sp->N, sp->csegs, sp->cnsegs*sizeof(XSegment));
847                sp->nold_segs[sp->c_old_seg] = sp->cnsegs;
848                sp->c_old_seg++;
849                if (sp->c_old_seg >= tail_len)
850                  sp->c_old_seg = 0;
851        }
852
853        if (++sp->count > MI_CYCLES(mi)) /* pick a new flow */
854                init_euler2d(mi);
855
856}
857
858void
859release_euler2d(ModeInfo * mi)
860{
861        if (euler2ds != NULL) {
862                int         screen;
863
864                for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
865                        free_euler2d(&euler2ds[screen]);
866                (void) free((void *) euler2ds);
867                euler2ds = (euler2dstruct *) NULL;
868        }
869}
870
871void
872refresh_euler2d(ModeInfo * mi)
873{
874        MI_CLEARWINDOW(mi);
875}
876
877#endif /* MODE_euler2d */
Note: See TracBrowser for help on using the repository browser.