• 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>
  • 相关阅读:
    2012 Multi-University #8
    2016"百度之星"
    Codeforces Round #352 (Div. 2)
    数位DP CF 55D Beautiful numbers
    数位DP GYM 100827 E Hill Number
    2012 Multi-University #9
    2012 Multi-University #10
    java生成指定范围的随机数
    MySql查询时间段的方法
    eclipse报错GC overhead limit exceed,卡顿
  • 原文地址:https://www.cnblogs.com/xiaoyang002/p/4072181.html
Copyright © 2020-2023  润新知