HTML5 canvas流体力学效果

某人用Java搞了一个流体力学的演示:http://grantkot.com/MPM/Liquid.html。

下面是 HTML 5版的流体力学演示(推荐使用Chrome浏览器浏览):

效果演示

不过,这仅仅是个开始。某同学将其发布上了reddit.com,于是,全世界的同学们开始给力了。

Flash的开发者首先不服,搞了个 flash版(带源码):http://wonderfl.net/c/yxe9

看到了Flash版,Javascript+HTML5的同学们也不干了,于是出现HTML5版(带源码):http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas

不过性能慢了很多,所以,又有人优化了一下HTML5版的程序:http://jsbin.com/unovo4

SVG的同学们也不甘寂寞,不过,那真叫一个慢啊:http://ulo.pe/js-liquid-svg/

这个时候,C/C++同学出来了,使用SDL库也搞了一个:http://q3k.org/fluidsim.zip

短短几天里,被人重写成各种语言。

下面看看在HTML 5里面的实现:

001 <canvas width="400" height="400" id="liquid"></canvas><script>
002 /**
003 * This version:
004 * Copyright Stephen Sinclair (radarsat1) ( http://www.music.mcgill.ca/~sinclair )
006 * Downloaded from: http://www.music.mcgill.ca/~sinclair/blog
007 */
008  
009 /**
010 * Flash version:
011 * Copyright iunpin ( http://wonderfl.net/user/iunpin )
013 * Downloaded from: http://wonderfl.net/c/6eu4
014 */
015  
016 /**
017 * Original Java version:
019 */
020  
021 var canvas;
022 var context;
023 var running = false;
024 var width = 0;
025 var height = 0;
026 var liquidTest;
027 var step = 0;
028  
029 function LiquidTest(gsizeX, gsizeY, particlesX, particlesY)
030 {
031     this.particles = [];
032  
033     this.gsizeX = gsizeX;
034     this.gsizeY = gsizeY;
035  
036     this.grid = [[]]; //Nodes
037     this.active = []; //Nodes
038     this.water = new Material(1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
039     this.pressed = false;
040     this.pressedprev = false;
041  
042     this.mx = 0;
043     this.my = 0;
044     this.mxprev = 0;
045     this.myprev = 0;
046  
047     this.init = function()
048     {
049         var i = 0, j = 0;
050         this.grid = [];
051         for (i = 0; i < this.gsizeX; i++)
052         {
053             this.grid.push([]);
054             for (j = 0; j < this.gsizeY; j++)
055             {
056                 this.grid[i].push(new Node());
057             }
058         }
059  
060         var p;
061         for (i = 0; i < particlesX; i++)
062             for (j = 0; j < particlesY; j++)
063             {
064                 p = new Particle(this.water, i + 4, j + 4, 0.0, 0.0);
065                 this.particles.push(p);
066             }
067     }
068  
069     this.paint = function()
070     {
071       context.clearRect(0, 0, width, height);
072  
073     context.beginPath();
074         for (var pi in this.particles)
075         {
076             var p = this.particles[pi];
077             line(4.0 * p.x,         4.0 * p.y,
078                  4.0 * (p.x - p.u), 4.0 * (p.y - p.v));
079         }
080  
081     context.stroke();
082     }
083  
084     this.simulate = function()
085     {
086         var drag = false;
087         var mdx = 0.0, mdy = 0.0;
088  
089         if (this.pressed && this.pressedprev)
090         {
091             drag = true;
092             mdx = 0.25 * (this.mx - this.mxprev);
093             mdy = 0.25 * (this.my - this.myprev);
094         }
095  
096         this.pressedprev = this.pressed;
097         this.mxprev = this.mx;
098         this.myprev = this.my;
099  
100         for (var n in this.active)
101             this.active[n].clear();
102         this.active.length = 0;
103  
104         var i, j;
105         var x, y, phi;
106         var fx = 0.0, fy = 0.0;
107         for (var pi in this.particles)
108         {
109             var p = this.particles[pi];
110             p.cx = parseInt(p.x - 0.5);
111             p.cy = parseInt(p.y - 0.5);
112  
113             x = p.cx - p.x;
114             p.px[0] = (0.5 * x * x + 1.5 * x + 1.125);
115             p.gx[0] = (x + 1.5);
116             x += 1.0;
117             p.px[1] = (-x * x + 0.75);
118             p.gx[1] = (-2.0 * x);
119             x += 1.0;
120             p.px[2] = (0.5 * x * x - 1.5 * x + 1.125);
121             p.gx[2] = (x - 1.5);
122  
123             y = p.cy - p.y;
124             p.py[0] = (0.5 * y * y + 1.5 * y + 1.125);
125             p.gy[0] = (y + 1.5);
126             y += 1.0;
127             p.py[1] = (-y * y + 0.75);
128             p.gy[1] = (-2.0 * y);
129             y += 1.0;
130             p.py[2] = (0.5 * y * y - 1.5 * y + 1.125);
131             p.gy[2] = (y - 1.5);
132  
133             for (var i = 0; i < 3; i++)
134             {
135                 for (var j = 0; j < 3; j++)
136                 {
137                     var n = this.grid[p.cx + i][p.cy + j];
138                     if (!n.active)
139                     {
140                         this.active.push(n);
141                         n.active = true;
142                     }
143                     phi = p.px[i] * p.py[j];
144                     n.m += phi * p.mat.m;
145                     n.d += phi;
146                     n.gx += p.gx[i] * p.py[j];
147                     n.gy += p.px[i] * p.gy[j];
148                 }
149             }
150         }
151  
152         var density, pressure, weight;
153         var n01, n02;
154         var n11, n12;
155         var cx, cy;
156         var cxi, cyi;
157  
158         var pdx, pdy;
159         var C20, C02, C30, C03;
160         var csum1, csum2;
161         var C21, C31, C12, C13, C11;
162  
163         var u, u2, u3;
164         var v, v2, v3;
165  
166         for (var pi in this.particles)
167         {
168             var p = this.particles[pi];
169  
170             cx = parseInt(p.x);
171             cy = parseInt(p.y);
172             cxi = cx + 1;
173             cyi = cy + 1;
174  
175             n01 = this.grid[cx][cy];
176             n02 = this.grid[cx][cyi];
177             n11 = this.grid[cxi][cy];
178             n12 = this.grid[cxi][cyi];
179  
180             pdx = n11.d - n01.d;
181             pdy = n02.d - n01.d;
182             C20 = 3.0 * pdx - n11.gx - 2.0 * n01.gx;
183             C02 = 3.0 * pdy - n02.gy - 2.0 * n01.gy;
184             C30 = -2.0 * pdx + n11.gx + n01.gx;
185             C03 = -2.0 * pdy + n02.gy + n01.gy;
186             csum1 = n01.d + n01.gy + C02 + C03;
187             csum2 = n01.d + n01.gx + C20 + C30;
188             C21 = 3.0 * n12.d - 2.0 * n02.gx - n12.gx - 3.0 * csum1 - C20;
189             C31 = -2.0 * n12.d + n02.gx + n12.gx + 2.0 * csum1 - C30;
190             C12 = 3.0 * n12.d - 2.0 * n11.gy - n12.gy - 3.0 * csum2 - C02;
191             C13 = -2.0 * n12.d + n11.gy + n12.gy + 2.0 * csum2 - C03;
192             C11 = n02.gx - C13 - C12 - n01.gx;
193  
194             u = p.x - cx;
195             u2 = u * u;
196             u3 = u * u2;
197             v = p.y - cy;
198             v2 = v * v;
199             v3 = v * v2;
200             density = n01.d + n01.gx * u + n01.gy * v + C20 * u2 + C02 * v2 + C30 * u3 + C03 * v3 + C21 * u2 * v + C31 * u3 * v + C12 * u * v2 + C13 * u * v3 + C11 * u * v;
201  
202             pressure = density - 1.0;
203             if (pressure > 2.0)
204                 pressure = 2.0;
205  
206             fx = 0.0;
207             fy = 0.0;
208  
209             if (p.x < 4.0)
210                 fx += p.mat.m * (4.0 - p.x);
211             else if (p.x > this.gsizeX - 5)
212                 fx += p.mat.m * (this.gsizeX - 5 - p.x);
213  
214             if (p.y < 4.0)
215                 fy += p.mat.m * (4.0 - p.y);
216             else if (p.y > this.gsizeY - 5)
217                 fy += p.mat.m * (this.gsizeY - 5 - p.y);
218  
219             if (drag)
220             {
221                 var vx = Math.abs(p.x - 0.25 * this.mx);
222                 var vy = Math.abs(p.y - 0.25 * this.my);
223                 if ((vx < 10.0) && (vy < 10.0))
224                 {
225                     weight = p.mat.m * (1.0 - vx * 0.10) * (1.0 - vy * 0.10);
226                     fx += weight * (mdx - p.u);
227                     fy += weight * (mdy - p.v);
228                 }
229             }
230  
231             for (i = 0; i < 3; i++)
232             {
233                 for (j = 0; j < 3; j++)
234                 {
235                     n = this.grid[(p.cx + i)][(p.cy + j)];
236                     phi = p.px[i] * p.py[j];
237                     n.ax += -((p.gx[i] * p.py[j]) * pressure) + fx * phi;
238                     n.ay += -((p.px[i] * p.gy[j]) * pressure) + fy * phi;
239                 }
240             }
241         }
242  
243         for (var ni in this.active)
244         {
245             var n = this.active[ni];
246             if (n.m > 0.0)
247             {
248                 n.ax /= n.m;
249                 n.ay /= n.m;
250                 n.ay += 0.03;
251             }
252         }
253  
254         var mu, mv;
255         for (var pi in this.particles)
256         {
257             var p = this.particles[pi];
258             for (i = 0; i < 3; i++)
259             {
260                 for (j = 0; j < 3; j++)
261                 {
262                     n = this.grid[(p.cx + i)][(p.cy + j)];
263                     phi = p.px[i] * p.py[j];
264                     p.u += phi * n.ax;
265                     p.v += phi * n.ay;
266                 }
267             }
268             mu = p.mat.m * p.u;
269             mv = p.mat.m * p.v;
270             for (i = 0; i < 3; i++)
271             {
272                 for (j = 0; j < 3; j++)
273                 {
274                     n = this.grid[(p.cx + i)][(p.cy + j)];
275                     phi = p.px[i] * p.py[j];
276                     n.u += phi * mu;
277                     n.v += phi * mv;
278                 }
279             }
280         }
281  
282         for (var ni in this.active)
283         {
284             var n = this.active[ni];
285             if (n.m > 0.0)
286             {
287                 n.u /= n.m;
288                 n.v /= n.m;
289             }
290         }
291  
292         var gu, gv;
293         for (var pi in this.particles)
294         {
295             var p = this.particles[pi];
296             gu = 0.0;
297             gv = 0.0;
298             for (var i = 0; i < 3; i++)
299             {
300                 for (var j = 0; j < 3; j++)
301                 {
302                     var n = this.grid[(p.cx + i)][(p.cy + j)];
303                     phi = p.px[i] * p.py[j];
304                     gu += phi * n.u;
305                     gv += phi * n.v;
306                 }
307             }
308             p.x += gu;
309             p.y += gv;
310             p.u += 1.0 * (gu - p.u);
311             p.v += 1.0 * (gv - p.v);
312             if (p.x < 1.0)
313             {
314                 p.x = (1.0 + Math.random() * 0.01);
315                 p.u = 0.0;
316             }
317             else if (p.x > this.gsizeX - 2)
318             {
319                 p.x = (this.gsizeX - 2 - Math.random() * 0.01);
320                 p.u = 0.0;
321             }
322             if (p.y < 1.0)
323             {
324                 p.y = (1.0 + Math.random() * 0.01);
325                 p.v = 0.0;
326             }
327             else if (p.y > this.gsizeY - 2)
328             {
329                 p.y = (this.gsizeY - 2 - Math.random() * 0.01);
330                 p.v = 0.0;
331             }
332         }
333     }
334  
335     this.init();
336 }
337  
338 function Node()
339 {
340     this.m = 0;
341     this.d = 0;
342     this.gx = 0;
343     this.gy = 0;
344     this.u = 0;
345     this.v = 0;
346     this.ax = 0;
347     this.ay = 0;
348     this.active = false;
349    
350     this.clear = function()
351     {
352         this.m = this.d = this.gx = this.gy = this.u = this.v = this.ax = this.ay = 0.0;
353         this.active = false;
354     }
355 }
356  
357 function Particle(mat, x, y, u, v)
358 {
359     this.mat = mat;
360     this.x = x;
361     this.y = y;
362     this.u = u;
363     this.v = v;
364  
365     this.dudx = 0;
366     this.dudy = 0;
367     this.dvdx = 0;
368     this.dvdy = 0;
369     this.cx = 0;
370     this.cy = 0;
371  
372     this.px = [0,0,0];
373     this.py = [0,0,0];
374     this.gx = [0,0,0];
375     this.gy = [0,0,0];
376 }
377  
378 function Material(m, rd, k, v, d, g)
379 {
380     this.m = m;
381     this.rd = rd;
382     this.k = k;
383     this.v = v;
384     this.d = d;
385     this.g = g;
386 }
387  
388 function line(x1,y1,x2,y2) {
389     context.moveTo(x1,y1);
390     context.lineTo(x2,y2);
391 }
392  
393 function getPosition(obj) {
394     var p = obj.offsetParent;
395     var left = obj.offsetLeft;
396     var top = obj.offsetTop;
397     if (p) {
398         var pos = getPosition(p);
399         left += pos[0];
400         top += pos[1];
401     }
402     return [left, top];
403 }
404  
405 function mouseMoved(event)
406 {
407     var pos = getPosition(canvas);
408     liquidTest.mx = event.pageX - pos[0];
409     liquidTest.my = event.pageY - pos[1];
410 }
411  
412 function mousePressed(event)
413 {
414     liquidTest.pressed = true;
415 }
416  
417 function mouseReleased(event)
418 {
419     liquidTest.pressed = false;
420 }
421  
422 function stop()
423 {
424     running = false;
425 }
426  
427 function start()
428 {
429     running = true;
430     draw();
431 }
432  
433 function restart(gsizeX, gsizeY, particlesX, particlesY)
434 {
435     liquidTest = new LiquidTest(gsizeX, gsizeY, particlesX, particlesY);
436     running = true;
437     draw();
438 }
439  
440 function draw()
441 {
442     // clear
443  
444     // advance simulation
445     liquidTest.simulate();
446  
447     step ++;
448 }
449  
450 function init() {
451     canvas = document.getElementById('liquid');
452     width = canvas.width;
453     height = canvas.height;
454     context = canvas.getContext('2d');
455     context.strokeStyle = "#0000FF";
456  
457     canvas.onmousedown = mousePressed;
458     canvas.onmouseup = mouseReleased;
459     canvas.onmousemove = mouseMoved;
460  
461     liquidTest = new LiquidTest(100, 100, 50, 50);
462  
463     start();
464 }
465  
466 setInterval(draw, 33);
467 setInterval("liquidTest.paint()", 33);
468  
469 init();
470 </script>
原文地址:https://www.cnblogs.com/xiaoyang002/p/4072181.html