首页 > 代码库 > HTML5 canvas流体力学效果

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里面的实现:

view source
 
print?
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 )
005 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
006 * Downloaded from: http://www.music.mcgill.ca/~sinclair/blog
007 */
008 
009/**
010 * Flash version:
011 * Copyright iunpin ( http://wonderfl.net/user/iunpin )
012 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
013 * Downloaded from: http://wonderfl.net/c/6eu4
014 */
015 
016/**
017 * Original Java version:
018 * http://grantkot.com/MPM/Liquid.html
019 */
020 
021var canvas;
022var context;
023var running = false;
024var width = 0;
025var height = 0;
026var liquidTest;
027var step = 0;
028 
029function 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 
338function 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 
357function 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 
378function 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 
388function line(x1,y1,x2,y2) {
389    context.moveTo(x1,y1);
390    context.lineTo(x2,y2);
391}
392 
393function 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 
405function mouseMoved(event)
406{
407    var pos = getPosition(canvas);
408    liquidTest.mx = event.pageX - pos[0];
409    liquidTest.my = event.pageY - pos[1];
410}
411 
412function mousePressed(event)
413{
414    liquidTest.pressed = true;
415}
416 
417function mouseReleased(event)
418{
419    liquidTest.pressed = false;
420}
421 
422function stop()
423{
424    running = false;
425}
426 
427function start()
428{
429    running = true;
430    draw();
431}
432 
433function restart(gsizeX, gsizeY, particlesX, particlesY)
434{
435    liquidTest = new LiquidTest(gsizeX, gsizeY, particlesX, particlesY);
436    running = true;
437    draw();
438}
439 
440function draw()
441{
442    // clear
443 
444    // advance simulation
445    liquidTest.simulate();
446 
447    step ++;
448}
449 
450function 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 
466setInterval(draw, 33);
467setInterval("liquidTest.paint()", 33);
468 
469init();
470</script>

HTML5 canvas流体力学效果