Previous page : Motion in the ocean… I mean in the atmosphere

The whole program
I have by purpose not used any frameworks or external modules, and also no OOP, even if I use objects. The reasons are that I can use it in the Programming 1 course I hold, where OOP is not part of the course, and also because I am not a great fan of OOP. Ok, here is the code:
'use strict'; /************************************************** * * A simulation of an atmosphere 1.4 * By Mauritz Blomqvist 2021-02-12. * This is free to use - but I'm happy if you add * a link to this page * https://properhoc.com/physics/themodynamics/a-simulation-of-an-atmosphere/ * in your code. * * This basically simulates a slice of an * atmosphere from the ground up to the * vacuum of space. * * In this 3000 "atoms", particles, are * undergoing totally elastic collision with * each other and the walls. * The particles are also influenced by gravity * so if they move upward they will slow down * until they eventually start to fall down again. * * The gas is initially in the lower end of the * atmosphere, and then they are "released". * After a minute or so the distribution of * particles keep somewhat constant. * **************************************************/ // Initialization of the canvas. const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.style = 'border:1px solid #0F0000;'; // The slice of space is 600 pixels wide // and 6000 pixels high. canvas.width = 600; canvas.height = 6000; // Number of particles. let n = 3000; // Free fall acceleration * dt. let gDt = 0.01; // The size of one atom - and related variables. let radius = 2; let diameter = 2 * radius; let diameter2 = (2 * radius) ** 2; // Initial speed of the atoms. let v0max = 3; // To speed up the calculations of the // particle-particle interactions the particles are // sorted in vertical slots of the same width // as the diameter of one atom (or slightly larger). // Then we only need to check the slot and // adjacent slots for let nSlots = Math.ceil(canvas.width / diameter); let slots; // A click on the restart button // will create new particles placed // in the bottom part of the canvas. document .getElementById('restart') .addEventListener('click', () => createParticles(n)); // This function is used to check if new particles overlap. function same(x, y) { for (let i = 0; i < particles.length; i++) { let d = Math.sqrt((particles[i].x - x) ** 2 + (particles[i].y - y) ** 2); if (d <= 2 * radius) return true; } return false; } // This will create new particles. // They will be given a random direction of // motion, and a random position within the lowest 500 // pixels of the canvas. // If a new particle overlaps with any already existing // a new position is tried until the particle // is placed at a previously empty position. let particles; function createParticles(n) { particles = []; let particle; for (let i = 0; i < n; i++) { let dir = Math.random() * 2 * Math.PI; do { particle = { x: Math.random() * (canvas.width - 2 * radius) + radius, y: canvas.height - Math.random() * (500 - 2 * radius) + radius, vx: v0max * Math.cos(dir), vy: v0max * Math.sin(dir), }; } while (same(particle.x, particle.y)); particles.push(particle); } } createParticles(n); // This will draw particle number i as a small circle. function drawParticle(i) { ctx.beginPath(); ctx.arc(particles[i].x, particles[i].y, radius, 0, 2 * Math.PI); ctx.stroke(); } // This will draw all particles. Particle // nr 0 will be red, all the rest will // be blue. function draw() { ctx.strokeStyle = 'RED'; drawParticle(0); ctx.strokeStyle = 'BLUE'; for (let i = 1; i < n; i++) { drawParticle(i); } } // This function moves the particles // and checks if a particle will bounce on a // wall. If so it will change the direction // of the perpendicular component of the // motion. If the particle moves in the vertical // direction the velocity will change due to // gravity. // As the particle moves say downward, it will change // its velocity by gdt, but the average velocity will be // (vOld+vNew)/2=(vOld+vOld+gdt)/2=vOld+gdt/2. // This can be seen in hte code in a few places. function move() { for (let i = 0; i < n; i++) { particles[i].x += particles[i].vx; particles[i].y += particles[i].vy + gDt / 2; if (particles[i].y < radius) { if (particles[i].vy < 0) particles[i].vy *= -1; particles[i].y += particles[i].vy - gDt / 2; } else if (particles[i].y > canvas.height - radius) { if (particles[i].vy > 0) particles[i].vy *= -1; particles[i].y += particles[i].vy - gDt / 2; } else particles[i].vy += gDt; if (particles[i].x < radius) { if (particles[i].vx < 0) particles[i].vx *= -1; particles[i].x += particles[i].vx; } else if (particles[i].x > canvas.width - radius) { if (particles[i].vx > 0) particles[i].vx *= -1; particles[i].x += particles[i].vx; } } } // This is to check if particle i and j // has collided. The horizontal distance between // the particles is checked to see if a collision // is possible, if so, then the actual distance is // checked. If a collision is still // possible then it is checked if the particles are moving // toward each other => collision, or not. // Then the calculations to simulate // a totally elastic collision is done. function collision(i, j) { let dy = particles[i].y - particles[j].y; if (Math.abs(dy) < diameter) { let dx = particles[i].x - particles[j].x; let dy = particles[i].y - particles[j].y; let d2 = dx ** 2 + dy ** 2; if (d2 < diameter2) { let dvx = particles[j].vx - particles[i].vx; let dvy = particles[j].vy - particles[i].vy; let dvs = dx * dvx + dy * dvy; if (dvs > 0) { dvs = dvs / d2; dvx = dvs * dx; dvy = dvs * dy; particles[i].vx += dvx; particles[i].vy += dvy; particles[j].vx -= dvx; particles[j].vy -= dvy; } } } } // In this function particle-particle interactions // is checked. First the particles will // be placed in vertical slots with a width // equal to (or slightly larger than) the // radius of the particles. // Then we loop over the slots, checking for // collisions among particles in the same slot, // and then with particles in the slot to the left. // I.e. the slots are checked all in 0 against all in 0, // all in 0 against all in 1, all in 1 against all in 1, // i.e. 0-0, 0-1, 1-1, 1-2, 2-2 and so on until the last slot // where we of course do not check against the non existent // next slot. // In this way we ensure that all possible pairs of particles // that could possibly collide are checked. function particleParticle() { slots = Array(nSlots) .fill(null) .map(() => []); // Place the particles in the correct slot. for (let i = 0; i < n; i++) { let s = Math.floor(particles[i].x / diameter); s = Math.max(s, 0); s = Math.min(s, nSlots - 1); slots[s].push(i); } for (let s = 0; s < nSlots; s++) { // Check all pairs of particles in the current slot. for (let i = 0; i < slots[s].length - 1; i++) { for (let j = i + 1; j < slots[s].length; j++) { collision(slots[s][i], slots[s][j]); } } // Check all particles in the current slot // against all particles in the slot to the right // unless we are at the last slot. if (s !== nSlots - 1) { for (let i = 0; i < slots[s].length; i++) { for (let j = 0; j < slots[s + 1].length; j++) { collision(slots[s][i], slots[s + 1][j]); } } } } } // Checks if the Escape key is pressed, if so // set running to false, that will cause // the main loop to halt. // If the escape key is pressed again the // main loop is called again. document.addEventListener('keydown', keyDown); let key = ''; let running = true; function keyDown(event) { key = event.key; if (key === 'Escape') { if (running) { running = false; } else { running = true; requestAnimationFrame(theMainLoop); } } } // The main loop. The canvas is cleared, // Then the particles are moved, and collisions // with the walls are done. // Next particle-particle interactions are checked // and done. // Then the particles are drawn. // If the running flag is set the main loop // is called again. function theMainLoop() { ctx.clearRect(0, 0, canvas.width, canvas.height); move(); particleParticle(); draw(); if (running) requestAnimationFrame(theMainLoop); } requestAnimationFrame(theMainLoop);
And the html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Atmosphere</title> </head> <body> <h2>A simulation of an atmosphere</h2> <p>Scroll down to see the lower end of the simulated atmosphere.</p> <p>For an explanation see <a href="https://properhoc.com/physics/themodynamics/a-simulation-of-an-atmosphere/"> here </a>.</p> <canvas id="canvas"></canvas> <br> <p id="p1"></p> <button id="restart">Restart</button> <script src="atmosphere4.js"></script> </body> </html>

Previous page : Motion in the ocean… I mean in the atmosphere
