1 | /* -*- Mode: C; tab-width: 4 -*- */ |
---|
2 | /* euler2d --- 2 Dimensional Incompressible Inviscid Fluid Flow */ |
---|
3 | |
---|
4 | #if 0 |
---|
5 | static 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 | |
---|
65 | static int tail_len; |
---|
66 | static int variable_boundary = 1; |
---|
67 | static float power = 1; |
---|
68 | |
---|
69 | static 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 | }; |
---|
76 | static 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 | }; |
---|
83 | static 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 | |
---|
89 | ModeSpecOpt euler2d_opts = |
---|
90 | {sizeof opts / sizeof opts[0], opts, |
---|
91 | sizeof vars / sizeof vars[0], vars, desc}; |
---|
92 | |
---|
93 | #ifdef USE_MODULES |
---|
94 | ModStruct 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 | |
---|
111 | static double delta_t; |
---|
112 | |
---|
113 | typedef 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 | |
---|
170 | static 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 | |
---|
185 | static void |
---|
186 | calc_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 */ |
---|
203 | static double |
---|
204 | calc_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 | |
---|
220 | static void |
---|
221 | calc_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 | |
---|
243 | static void |
---|
244 | calc_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 | |
---|
264 | static void |
---|
265 | derivs(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 | |
---|
382 | static void |
---|
383 | perturb(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 | |
---|
449 | static void |
---|
450 | ode_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 | |
---|
488 | static void |
---|
489 | free_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 | |
---|
509 | void |
---|
510 | init_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 | |
---|
749 | void |
---|
750 | draw_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 | |
---|
858 | void |
---|
859 | release_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 | |
---|
871 | void |
---|
872 | refresh_euler2d(ModeInfo * mi) |
---|
873 | { |
---|
874 | MI_CLEARWINDOW(mi); |
---|
875 | } |
---|
876 | |
---|
877 | #endif /* MODE_euler2d */ |
---|