Advertisement
here2share

EulerFluid.html

Aug 17th, 2024
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!--
  2. EulerFluid.html
  3.  
  4. Copyright 2022 Matthias Müller - Ten Minute Physics,
  5. www.youtube.com/c/TenMinutePhysics
  6. www.matthiasMueller.info/tenMinutePhysics
  7.  
  8. MIT License
  9.  
  10. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  11.  
  12. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  13.  
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  15. -->
  16.  
  17. <!DOCTYPE html>
  18. <html>
  19.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  20.  
  21.     <head>
  22.         <title>Euler Fluid</title>
  23.         <style>
  24.             body {
  25.                 font-family: verdana;
  26.                 font-size: 15px;
  27.             }          
  28.             .button {
  29.                 background-color: #606060;
  30.                 border: none;
  31.                 color: white;
  32.                 padding: 10px 10px;
  33.                 font-size: 16px;
  34.                 margin: 4px 2px;
  35.                 cursor: pointer;
  36.             }
  37.             .slider {
  38.                 -webkit-appearance: none;
  39.                 width: 80px;
  40.                 height: 6px;
  41.                 border-radius: 5px;
  42.                 background: #d3d3d3;
  43.                 outline: none;
  44.                 opacity: 0.7;
  45.                 -webkit-transition: .2s;
  46.                 transition: opacity .2s;
  47.             }
  48.         </style>
  49.     </head>
  50.    
  51. <body>
  52.  
  53.     <button class="button" onclick="setupScene(1)">Wind Tunnel</button>
  54.     <button class="button" onclick="setupScene(3)">Hires Tunnel</button>
  55.     <button class="button" onclick="setupScene(0)">Tank</button>
  56.     <button class="button" onclick="setupScene(2)">Paint</button>
  57.     <input type = "checkbox" id = "streamButton" onclick = "scene.showStreamlines = !scene.showStreamlines">Streamlines
  58.     <input type = "checkbox" id = "velocityButton" onclick = "scene.showVelocities = !scene.showVelocities">Velocities
  59.     <input type = "checkbox" name = "field" id = "pressureButton" onclick = "scene.showPressure = !scene.showPressure;"> Pressure
  60.     <input type = "checkbox" name = "field" id = "smokeButton" onclick = "scene.showSmoke = !scene.showSmoke;" checked>Smoke
  61.     <input type = "checkbox" id = "overrelaxButton" onclick = "scene.overRelaxation = scene.overRelaxation == 1.0 ? 1.9 : 1.0" checked>Overrelax
  62.     <br>
  63.     <canvas id="myCanvas" style="border:2px solid"></canvas>
  64.    
  65. <script>
  66.  
  67.     var canvas = document.getElementById("myCanvas");
  68.     var c = canvas.getContext("2d");   
  69.     canvas.width = window.innerWidth - 20;
  70.     canvas.height = window.innerHeight - 100;
  71.  
  72.     canvas.focus();
  73.  
  74.     var simHeight = 1.1;   
  75.     var cScale = canvas.height / simHeight;
  76.     var simWidth = canvas.width / cScale;
  77.  
  78.     var U_FIELD = 0;
  79.     var V_FIELD = 1;
  80.     var S_FIELD = 2;
  81.  
  82.     var cnt = 0;
  83.  
  84.     function cX(x) {
  85.         return x * cScale;
  86.     }
  87.  
  88.     function cY(y) {
  89.         return canvas.height - y * cScale;
  90.     }
  91.  
  92.     // ----------------- start of simulator ------------------------------
  93.  
  94.     class Fluid {
  95.         constructor(density, numX, numY, h) {
  96.             this.density = density;
  97.             this.numX = numX + 2;
  98.             this.numY = numY + 2;
  99.             this.numCells = this.numX * this.numY;
  100.             this.h = h;
  101.             this.u = new Float32Array(this.numCells);
  102.             this.v = new Float32Array(this.numCells);
  103.             this.newU = new Float32Array(this.numCells);
  104.             this.newV = new Float32Array(this.numCells);
  105.             this.p = new Float32Array(this.numCells);
  106.             this.s = new Float32Array(this.numCells);
  107.             this.m = new Float32Array(this.numCells);
  108.             this.newM = new Float32Array(this.numCells);
  109.             this.m.fill(1.0)
  110.             var num = numX * numY;
  111.         }
  112.  
  113.         integrate(dt, gravity) {
  114.             var n = this.numY;
  115.             for (var i = 1; i < this.numX; i++) {
  116.                 for (var j = 1; j < this.numY-1; j++) {
  117.                     if (this.s[i*n + j] != 0.0 && this.s[i*n + j-1] != 0.0)
  118.                         this.v[i*n + j] += gravity * dt;
  119.                 }    
  120.             }
  121.         }
  122.  
  123.         solveIncompressibility(numIters, dt) {
  124.  
  125.             var n = this.numY;
  126.             var cp = this.density * this.h / dt;
  127.  
  128.             for (var iter = 0; iter < numIters; iter++) {
  129.  
  130.                 for (var i = 1; i < this.numX-1; i++) {
  131.                     for (var j = 1; j < this.numY-1; j++) {
  132.  
  133.                         if (this.s[i*n + j] == 0.0)
  134.                             continue;
  135.  
  136.                         var s = this.s[i*n + j];
  137.                         var sx0 = this.s[(i-1)*n + j];
  138.                         var sx1 = this.s[(i+1)*n + j];
  139.                         var sy0 = this.s[i*n + j-1];
  140.                         var sy1 = this.s[i*n + j+1];
  141.                         var s = sx0 + sx1 + sy0 + sy1;
  142.                         if (s == 0.0)
  143.                             continue;
  144.  
  145.                         var div = this.u[(i+1)*n + j] - this.u[i*n + j] +
  146.                             this.v[i*n + j+1] - this.v[i*n + j];
  147.  
  148.                         var p = -div / s;
  149.                         p *= scene.overRelaxation;
  150.                         this.p[i*n + j] += cp * p;
  151.  
  152.                         this.u[i*n + j] -= sx0 * p;
  153.                         this.u[(i+1)*n + j] += sx1 * p;
  154.                         this.v[i*n + j] -= sy0 * p;
  155.                         this.v[i*n + j+1] += sy1 * p;
  156.                     }
  157.                 }
  158.             }
  159.         }
  160.  
  161.         extrapolate() {
  162.             var n = this.numY;
  163.             for (var i = 0; i < this.numX; i++) {
  164.                 this.u[i*n + 0] = this.u[i*n + 1];
  165.                 this.u[i*n + this.numY-1] = this.u[i*n + this.numY-2];
  166.             }
  167.             for (var j = 0; j < this.numY; j++) {
  168.                 this.v[0*n + j] = this.v[1*n + j];
  169.                 this.v[(this.numX-1)*n + j] = this.v[(this.numX-2)*n + j]
  170.             }
  171.         }
  172.  
  173.         sampleField(x, y, field) {
  174.             var n = this.numY;
  175.             var h = this.h;
  176.             var h1 = 1.0 / h;
  177.             var h2 = 0.5 * h;
  178.  
  179.             x = Math.max(Math.min(x, this.numX * h), h);
  180.             y = Math.max(Math.min(y, this.numY * h), h);
  181.  
  182.             var dx = 0.0;
  183.             var dy = 0.0;
  184.  
  185.             var f;
  186.  
  187.             switch (field) {
  188.                 case U_FIELD: f = this.u; dy = h2; break;
  189.                 case V_FIELD: f = this.v; dx = h2; break;
  190.                 case S_FIELD: f = this.m; dx = h2; dy = h2; break
  191.  
  192.             }
  193.  
  194.             var x0 = Math.min(Math.floor((x-dx)*h1), this.numX-1);
  195.             var tx = ((x-dx) - x0*h) * h1;
  196.             var x1 = Math.min(x0 + 1, this.numX-1);
  197.            
  198.             var y0 = Math.min(Math.floor((y-dy)*h1), this.numY-1);
  199.             var ty = ((y-dy) - y0*h) * h1;
  200.             var y1 = Math.min(y0 + 1, this.numY-1);
  201.  
  202.             var sx = 1.0 - tx;
  203.             var sy = 1.0 - ty;
  204.  
  205.             var val = sx*sy * f[x0*n + y0] +
  206.                 tx*sy * f[x1*n + y0] +
  207.                 tx*ty * f[x1*n + y1] +
  208.                 sx*ty * f[x0*n + y1];
  209.            
  210.             return val;
  211.         }
  212.  
  213.         avgU(i, j) {
  214.             var n = this.numY;
  215.             var u = (this.u[i*n + j-1] + this.u[i*n + j] +
  216.                 this.u[(i+1)*n + j-1] + this.u[(i+1)*n + j]) * 0.25;
  217.             return u;
  218.                
  219.         }
  220.  
  221.         avgV(i, j) {
  222.             var n = this.numY;
  223.             var v = (this.v[(i-1)*n + j] + this.v[i*n + j] +
  224.                 this.v[(i-1)*n + j+1] + this.v[i*n + j+1]) * 0.25;
  225.             return v;
  226.         }
  227.  
  228.         advectVel(dt) {
  229.  
  230.             this.newU.set(this.u);
  231.             this.newV.set(this.v);
  232.  
  233.             var n = this.numY;
  234.             var h = this.h;
  235.             var h2 = 0.5 * h;
  236.  
  237.             for (var i = 1; i < this.numX; i++) {
  238.                 for (var j = 1; j < this.numY; j++) {
  239.  
  240.                     cnt++;
  241.  
  242.                     // u component
  243.                     if (this.s[i*n + j] != 0.0 && this.s[(i-1)*n + j] != 0.0 && j < this.numY - 1) {
  244.                         var x = i*h;
  245.                         var y = j*h + h2;
  246.                         var u = this.u[i*n + j];
  247.                         var v = this.avgV(i, j);
  248. //                      var v = this.sampleField(x,y, V_FIELD);
  249.                         x = x - dt*u;
  250.                         y = y - dt*v;
  251.                         u = this.sampleField(x,y, U_FIELD);
  252.                         this.newU[i*n + j] = u;
  253.                     }
  254.                     // v component
  255.                     if (this.s[i*n + j] != 0.0 && this.s[i*n + j-1] != 0.0 && i < this.numX - 1) {
  256.                         var x = i*h + h2;
  257.                         var y = j*h;
  258.                         var u = this.avgU(i, j);
  259. //                      var u = this.sampleField(x,y, U_FIELD);
  260.                         var v = this.v[i*n + j];
  261.                         x = x - dt*u;
  262.                         y = y - dt*v;
  263.                         v = this.sampleField(x,y, V_FIELD);
  264.                         this.newV[i*n + j] = v;
  265.                     }
  266.                 }    
  267.             }
  268.  
  269.             this.u.set(this.newU);
  270.             this.v.set(this.newV);
  271.         }
  272.  
  273.         advectSmoke(dt) {
  274.  
  275.             this.newM.set(this.m);
  276.  
  277.             var n = this.numY;
  278.             var h = this.h;
  279.             var h2 = 0.5 * h;
  280.  
  281.             for (var i = 1; i < this.numX-1; i++) {
  282.                 for (var j = 1; j < this.numY-1; j++) {
  283.  
  284.                     if (this.s[i*n + j] != 0.0) {
  285.                         var u = (this.u[i*n + j] + this.u[(i+1)*n + j]) * 0.5;
  286.                         var v = (this.v[i*n + j] + this.v[i*n + j+1]) * 0.5;
  287.                         var x = i*h + h2 - dt*u;
  288.                         var y = j*h + h2 - dt*v;
  289.  
  290.                         this.newM[i*n + j] = this.sampleField(x,y, S_FIELD);
  291.                     }
  292.                 }    
  293.             }
  294.             this.m.set(this.newM);
  295.         }
  296.  
  297.         // ----------------- end of simulator ------------------------------
  298.  
  299.  
  300.         simulate(dt, gravity, numIters) {
  301.  
  302.             this.integrate(dt, gravity);
  303.  
  304.             this.p.fill(0.0);
  305.             this.solveIncompressibility(numIters, dt);
  306.  
  307.             this.extrapolate();
  308.             this.advectVel(dt);
  309.             this.advectSmoke(dt);
  310.         }
  311.     }
  312.  
  313.     var scene =
  314.     {
  315.         gravity : -9.81,
  316.         dt : 1.0 / 120.0,
  317.         numIters : 100,
  318.         frameNr : 0,
  319.         overRelaxation : 1.9,
  320.         obstacleX : 0.0,
  321.         obstacleY : 0.0,
  322.         obstacleRadius: 0.15,
  323.         paused: false,
  324.         sceneNr: 0,
  325.         showObstacle: false,
  326.         showStreamlines: false,
  327.         showVelocities: false, 
  328.         showPressure: false,
  329.         showSmoke: true,
  330.         fluid: null
  331.     };
  332.  
  333.     function setupScene(sceneNr = 0)
  334.     {
  335.         scene.sceneNr = sceneNr;
  336.         scene.obstacleRadius = 0.15;
  337.         scene.overRelaxation = 1.9;
  338.  
  339.         scene.dt = 1.0 / 60.0;
  340.         scene.numIters = 40;
  341.  
  342.         var res = 100;
  343.        
  344.         if (sceneNr == 0)
  345.             res = 50;
  346.         else if (sceneNr == 3)
  347.             res = 200;
  348.  
  349.         var domainHeight = 1.0;
  350.         var domainWidth = domainHeight / simHeight * simWidth;
  351.         var h = domainHeight / res;
  352.  
  353.         var numX = Math.floor(domainWidth / h);
  354.         var numY = Math.floor(domainHeight / h);
  355.  
  356.         var density = 1000.0;
  357.  
  358.         f = scene.fluid = new Fluid(density, numX, numY, h);
  359.  
  360.         var n = f.numY;
  361.  
  362.         if (sceneNr == 0) {         // tank
  363.  
  364.             for (var i = 0; i < f.numX; i++) {
  365.                 for (var j = 0; j < f.numY; j++) {
  366.                     var s = 1.0;    // fluid
  367.                     if (i == 0 || i == f.numX-1 || j == 0)
  368.                         s = 0.0;    // solid
  369.                     f.s[i*n + j] = s
  370.                 }
  371.             }
  372.             scene.gravity = -9.81;
  373.             scene.showPressure = true;
  374.             scene.showSmoke = false;
  375.             scene.showStreamlines = false;
  376.             scene.showVelocities = false;
  377.         }
  378.         else if (sceneNr == 1 || sceneNr == 3) { // vortex shedding
  379.  
  380.             var inVel = 2.0;
  381.             for (var i = 0; i < f.numX; i++) {
  382.                 for (var j = 0; j < f.numY; j++) {
  383.                     var s = 1.0;    // fluid
  384.                     if (i == 0 || j == 0 || j == f.numY-1)
  385.                         s = 0.0;    // solid
  386.                     f.s[i*n + j] = s
  387.  
  388.                     if (i == 1) {
  389.                         f.u[i*n + j] = inVel;
  390.                     }
  391.                 }
  392.             }
  393.  
  394.             var pipeH = 0.1 * f.numY;
  395.             var minJ = Math.floor(0.5 * f.numY - 0.5*pipeH);
  396.             var maxJ = Math.floor(0.5 * f.numY + 0.5*pipeH);
  397.  
  398.             for (var j = minJ; j < maxJ; j++)
  399.                 f.m[j] = 0.0;
  400.  
  401.             setObstacle(0.4, 0.5, true)
  402.  
  403.             scene.gravity = 0.0;
  404.             scene.showPressure = false;
  405.             scene.showSmoke = true;
  406.             scene.showStreamlines = false;
  407.             scene.showVelocities = false;
  408.  
  409.             if (sceneNr == 3) {
  410.                 scene.dt = 1.0 / 120.0;
  411.                 scene.numIters = 100;
  412.                 scene.showPressure = true;
  413.             }
  414.  
  415.         }
  416.         else if (sceneNr == 2) { // paint
  417.  
  418.             scene.gravity = 0.0;
  419.             scene.overRelaxation = 1.0;
  420.             scene.showPressure = false;
  421.             scene.showSmoke = true;
  422.             scene.showStreamlines = false;
  423.             scene.showVelocities = false;
  424.             scene.obstacleRadius = 0.1;
  425.         }
  426.  
  427.         document.getElementById("streamButton").checked = scene.showStreamlines;
  428.         document.getElementById("velocityButton").checked = scene.showVelocities;
  429.         document.getElementById("pressureButton").checked = scene.showPressure;
  430.         document.getElementById("smokeButton").checked = scene.showSmoke;
  431.         document.getElementById("overrelaxButton").checked = scene.overRelaxation > 1.0;
  432.        
  433.     }
  434.  
  435.  
  436.     // draw -------------------------------------------------------
  437.  
  438.     function setColor(r,g,b) {
  439.         c.fillStyle = `rgb(
  440.             ${Math.floor(255*r)},
  441.             ${Math.floor(255*g)},
  442.             ${Math.floor(255*b)})`
  443.         c.strokeStyle = `rgb(
  444.             ${Math.floor(255*r)},
  445.             ${Math.floor(255*g)},
  446.             ${Math.floor(255*b)})`
  447.     }
  448.  
  449.     function getSciColor(val, minVal, maxVal) {
  450.         val = Math.min(Math.max(val, minVal), maxVal- 0.0001);
  451.         var d = maxVal - minVal;
  452.         val = d == 0.0 ? 0.5 : (val - minVal) / d;
  453.         var m = 0.25;
  454.         var num = Math.floor(val / m);
  455.         var s = (val - num * m) / m;
  456.         var r, g, b;
  457.  
  458.         switch (num) {
  459.             case 0 : r = 0.0; g = s; b = 1.0; break;
  460.             case 1 : r = 0.0; g = 1.0; b = 1.0-s; break;
  461.             case 2 : r = s; g = 1.0; b = 0.0; break;
  462.             case 3 : r = 1.0; g = 1.0 - s; b = 0.0; break;
  463.         }
  464.  
  465.         return[255*r,255*g,255*b, 255]
  466.     }
  467.  
  468.     function draw()
  469.     {
  470.         c.clearRect(0, 0, canvas.width, canvas.height);
  471.  
  472.         c.fillStyle = "#FF0000";
  473.         f = scene.fluid;
  474.         n = f.numY;
  475.  
  476.         var cellScale = 1.1;
  477.  
  478.         var h = f.h;
  479.  
  480.         minP = f.p[0];
  481.         maxP = f.p[0];
  482.  
  483.         for (var i = 0; i < f.numCells; i++) {
  484.             minP = Math.min(minP, f.p[i]);
  485.             maxP = Math.max(maxP, f.p[i]);
  486.         }
  487.  
  488.         id = c.getImageData(0,0, canvas.width, canvas.height)
  489.  
  490.         var color = [255, 255, 255, 255]
  491.  
  492.         for (var i = 0; i < f.numX; i++) {
  493.             for (var j = 0; j < f.numY; j++) {
  494.  
  495.                 if (scene.showPressure) {
  496.                     var p = f.p[i*n + j];
  497.                     var s = f.m[i*n + j];
  498.                     color = getSciColor(p, minP, maxP);
  499.                     if (scene.showSmoke) {
  500.                         color[0] = Math.max(0.0, color[0] - 255*s);
  501.                         color[1] = Math.max(0.0, color[1] - 255*s);
  502.                         color[2] = Math.max(0.0, color[2] - 255*s);
  503.                     }
  504.                 }
  505.                 else if (scene.showSmoke) {
  506.                     var s = f.m[i*n + j];
  507.                     color[0] = 255*s;
  508.                     color[1] = 255*s;
  509.                     color[2] = 255*s;
  510.                     if (scene.sceneNr == 2)
  511.                         color = getSciColor(s, 0.0, 1.0);
  512.                 }
  513.                 else if (f.s[i*n + j] == 0.0) {
  514.                     color[0] = 0;
  515.                     color[1] = 0;
  516.                     color[2] = 0;
  517.                 }
  518.  
  519.                 var x = Math.floor(cX(i * h));
  520.                 var y = Math.floor(cY((j+1) * h));
  521.                 var cx = Math.floor(cScale * cellScale * h) + 1;
  522.                 var cy = Math.floor(cScale * cellScale * h) + 1;
  523.  
  524.                 r = color[0];
  525.                 g = color[1];
  526.                 b = color[2];
  527.  
  528.                 for (var yi = y; yi < y + cy; yi++) {
  529.                     var p = 4 * (yi * canvas.width + x)
  530.  
  531.                     for (var xi = 0; xi < cx; xi++) {
  532.                         id.data[p++] = r;
  533.                         id.data[p++] = g;
  534.                         id.data[p++] = b;
  535.                         id.data[p++] = 255;
  536.                     }
  537.                 }
  538.             }
  539.         }
  540.  
  541.         c.putImageData(id, 0, 0);
  542.  
  543.         if (scene.showVelocities) {
  544.  
  545.             c.strokeStyle = "#000000"; 
  546.             scale = 0.02;  
  547.  
  548.             for (var i = 0; i < f.numX; i++) {
  549.                 for (var j = 0; j < f.numY; j++) {
  550.  
  551.                     var u = f.u[i*n + j];
  552.                     var v = f.v[i*n + j];
  553.  
  554.                     c.beginPath();
  555.  
  556.                     x0 = cX(i * h);
  557.                     x1 = cX(i * h + u * scale);
  558.                     y = cY((j + 0.5) * h );
  559.  
  560.                     c.moveTo(x0, y);
  561.                     c.lineTo(x1, y);
  562.                     c.stroke();
  563.  
  564.                     x = cX((i + 0.5) * h);
  565.                     y0 = cY(j * h );
  566.                     y1 = cY(j * h + v * scale)
  567.  
  568.                     c.beginPath();
  569.                     c.moveTo(x, y0);
  570.                     c.lineTo(x, y1);
  571.                     c.stroke();
  572.  
  573.                 }
  574.             }
  575.         }
  576.  
  577.         if (scene.showStreamlines) {
  578.  
  579.             var segLen = f.h * 0.2;
  580.             var numSegs = 15;
  581.  
  582.             c.strokeStyle = "#000000";
  583.  
  584.             for (var i = 1; i < f.numX - 1; i += 5) {
  585.                 for (var j = 1; j < f.numY - 1; j += 5) {
  586.  
  587.                     var x = (i + 0.5) * f.h;
  588.                     var y = (j + 0.5) * f.h;
  589.  
  590.                     c.beginPath();
  591.                     c.moveTo(cX(x), cY(y));
  592.  
  593.                     for (var n = 0; n < numSegs; n++) {
  594.                         var u = f.sampleField(x, y, U_FIELD);
  595.                         var v = f.sampleField(x, y, V_FIELD);
  596.                         l = Math.sqrt(u*u + v*v);
  597.                         // x += u/l * segLen;
  598.                         // y += v/l * segLen;
  599.                         x += u * 0.01;
  600.                         y += v * 0.01;
  601.                         if (x > f.numX * f.h)
  602.                             break;
  603.  
  604.                         c.lineTo(cX(x), cY(y));
  605.                     }
  606.                     c.stroke();
  607.                 }
  608.             }
  609.         }
  610.  
  611.         if (scene.showObstacle) {
  612.  
  613.             c.strokeW
  614.             r = scene.obstacleRadius + f.h;
  615.             if (scene.showPressure)
  616.                 c.fillStyle = "#000000";
  617.             else
  618.                 c.fillStyle = "#DDDDDD";
  619.             c.beginPath(); 
  620.             c.arc(
  621.                 cX(scene.obstacleX), cY(scene.obstacleY), cScale * r, 0.0, 2.0 * Math.PI);
  622.             c.closePath();
  623.             c.fill();
  624.  
  625.             c.lineWidth = 3.0;
  626.             c.strokeStyle = "#000000";
  627.             c.beginPath(); 
  628.             c.arc(
  629.                 cX(scene.obstacleX), cY(scene.obstacleY), cScale * r, 0.0, 2.0 * Math.PI);
  630.             c.closePath();
  631.             c.stroke();
  632.             c.lineWidth = 1.0;
  633.         }
  634.  
  635.         if (scene.showPressure) {
  636.             var s = "pressure: " + minP.toFixed(0) + " - " + maxP.toFixed(0) + " N/m";
  637.             c.fillStyle ="#000000";
  638.             c.font = "16px Arial";
  639.             c.fillText(s, 10, 35);
  640.         }
  641.     }
  642.  
  643.     function setObstacle(x, y, reset) {
  644.  
  645.         var vx = 0.0;
  646.         var vy = 0.0;
  647.  
  648.         if (!reset) {
  649.             vx = (x - scene.obstacleX) / scene.dt;
  650.             vy = (y - scene.obstacleY) / scene.dt;
  651.         }
  652.  
  653.         scene.obstacleX = x;
  654.         scene.obstacleY = y;
  655.         var r = scene.obstacleRadius;
  656.         var f = scene.fluid;
  657.         var n = f.numY;
  658.         var cd = Math.sqrt(2) * f.h;
  659.  
  660.         for (var i = 1; i < f.numX-2; i++) {
  661.             for (var j = 1; j < f.numY-2; j++) {
  662.  
  663.                 f.s[i*n + j] = 1.0;
  664.  
  665.                 dx = (i + 0.5) * f.h - x;
  666.                 dy = (j + 0.5) * f.h - y;
  667.  
  668.                 if (dx * dx + dy * dy < r * r) {
  669.                     f.s[i*n + j] = 0.0;
  670.                     if (scene.sceneNr == 2)
  671.                         f.m[i*n + j] = 0.5 + 0.5 * Math.sin(0.1 * scene.frameNr)
  672.                     else
  673.                         f.m[i*n + j] = 1.0;
  674.                     f.u[i*n + j] = vx;
  675.                     f.u[(i+1)*n + j] = vx;
  676.                     f.v[i*n + j] = vy;
  677.                     f.v[i*n + j+1] = vy;
  678.                 }
  679.             }
  680.         }
  681.        
  682.         scene.showObstacle = true;
  683.     }
  684.  
  685.     // interaction -------------------------------------------------------
  686.  
  687.     var mouseDown = false;
  688.  
  689.     function startDrag(x, y) {
  690.         let bounds = canvas.getBoundingClientRect();
  691.  
  692.         let mx = x - bounds.left - canvas.clientLeft;
  693.         let my = y - bounds.top - canvas.clientTop;
  694.         mouseDown = true;
  695.  
  696.         x = mx / cScale;
  697.         y = (canvas.height - my) / cScale;
  698.  
  699.         setObstacle(x,y, true);
  700.     }
  701.  
  702.     function drag(x, y) {
  703.         if (mouseDown) {
  704.             let bounds = canvas.getBoundingClientRect();
  705.             let mx = x - bounds.left - canvas.clientLeft;
  706.             let my = y - bounds.top - canvas.clientTop;
  707.             x = mx / cScale;
  708.             y = (canvas.height - my) / cScale;
  709.             setObstacle(x,y, false);
  710.         }
  711.     }
  712.  
  713.     function endDrag() {
  714.         mouseDown = false;
  715.     }
  716.  
  717.     canvas.addEventListener('mousedown', event => {
  718.         startDrag(event.x, event.y);
  719.     });
  720.  
  721.     canvas.addEventListener('mouseup', event => {
  722.         endDrag();
  723.     });
  724.  
  725.     canvas.addEventListener('mousemove', event => {
  726.         drag(event.x, event.y);
  727.     });
  728.  
  729.     canvas.addEventListener('touchstart', event => {
  730.         startDrag(event.touches[0].clientX, event.touches[0].clientY)
  731.     });
  732.  
  733.     canvas.addEventListener('touchend', event => {
  734.         endDrag()
  735.     });
  736.  
  737.     canvas.addEventListener('touchmove', event => {
  738.         event.preventDefault();
  739.         event.stopImmediatePropagation();
  740.         drag(event.touches[0].clientX, event.touches[0].clientY)
  741.     }, { passive: false});
  742.  
  743.  
  744.     document.addEventListener('keydown', event => {
  745.         switch(event.key) {
  746.             case 'p': scene.paused = !scene.paused; break;
  747.             case 'm': scene.paused = false; simulate(); scene.paused = true; break;
  748.         }
  749.     });
  750.  
  751.     function toggleStart()
  752.     {
  753.         var button = document.getElementById('startButton');
  754.         if (scene.paused)
  755.             button.innerHTML = "Stop";
  756.         else
  757.             button.innerHTML = "Start";
  758.         scene.paused = !scene.paused;
  759.     }
  760.  
  761.     // main -------------------------------------------------------
  762.  
  763.     function simulate()
  764.     {
  765.         if (!scene.paused)
  766.             scene.fluid.simulate(scene.dt, scene.gravity, scene.numIters)
  767.             scene.frameNr++;
  768.     }
  769.  
  770.     function update() {
  771.         simulate();
  772.         draw();
  773.         requestAnimationFrame(update);
  774.     }
  775.    
  776.     setupScene(1);
  777.     update();
  778.    
  779. </script>
  780. </body>
  781. </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement