Skip to content

Instantly share code, notes, and snippets.

@TylerLeite
Last active November 4, 2020 18:17
Show Gist options
  • Save TylerLeite/12da2db952067fb20dbd224cf945bd4d to your computer and use it in GitHub Desktop.
Save TylerLeite/12da2db952067fb20dbd224cf945bd4d to your computer and use it in GitHub Desktop.
Gravity
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>moonshot</title>
<style>
* {padding: 0; margin: 0; outline: 0;}
body {display: flex; align-items: center; justify-content: center; margin-top: 5vh}
canvas {width: 800px; height: 800px;}
</style>
</head>
<body>
<canvas id="canvas" width="6400px" height="6400px"></canvas>
<script type="text/javascript">
const canvas = document.getElementById('canvas');
canvas.addEventListener('mousedown', (e) => {
const rect = e.path[0].getBoundingClientRect();
const x = (e.clientX - rect.x)/rect.width;
const y = (e.clientY - rect.y)/rect.height;
const planet = addPlanet(randomPlanetMaterial(), x, y);
planet.xVel = Math.random()*1e-6-5e-7
planet.yVel = Math.random()*1e-6-5e-7
planets.push(planet);
// console.log(planet)
});
canvas.addEventListener('mouseup', (e) => {
return;
});
canvas.addEventListener('mousemove', (e) => {
return;
});
const ctx = canvas.getContext('2d');
const sz = 6400; // aka 9 billion kilometers
const materials = {
'gas': {
color: '#d4bf39',
density: 1500, // kg/m^3
// 69,911
radius: () => 7.6e-4, // as a fraction of 9b km
},
'water': {
color: '#3699e0',
density: 1000,
radius: () => 7.1e-5,
},
'rock': {
color: '#825507',
density: 5500,
radius: () => 3.8e-5,
},
'iron': {
color: '#8a8a8a',
density: 10000,
radius: () => 3.9e-5,
},
'neutrons': {
color: '#eeeeee',
density: 1e17,
radius: () => 1.1e-7,
},
'???': {
color: '#000000',
density: 2e17,
radius: () => 1.1e-7,
},
'ship': {
color: '#ff45f9',
density: 1473,
radius: 4.1e-10,
},
}
planets = [];
function addPlanet (material, xPos=Math.random(), yPos=Math.random(), radius=-1) {
const info = materials[material];
if (radius == -1) {
radius = info.radius()*sz;
}
const planet = {
material,
mass: info.density*radius*radius*radius,
xPos: xPos*sz,
yPos: yPos*sz,
xVel: 0,
yVel: 0,
radius,
}
return planet;
}
function drawPlanet (material, xPos, yPos, radius) {
radius = Math.max(radius, 8);
ctx.strokeStyle = materials[material].color;
ctx.lineWidth = '12';
ctx.beginPath();
ctx.arc(xPos, yPos, radius, 0, 2*Math.PI);
ctx.stroke();
}
function drawShip (xPos, yPos) {
drawPlanet('ship', xPos, yPos, 0.0005*sz);
}
function randomPlanetMaterial () {
const mats = Object.keys(materials).filter(e => e != 'ship');
return mats[Math.floor(Math.random()*mats.length)];
}
for (let i = 0; i < 2; i++) {
planets.push(addPlanet(randomPlanetMaterial()));
}
let ship = {
material: 'ship',
xPos: 0.25*sz,
yPos: 0.75*sz,
xVel: 1.2e-7,
yVel: -1.2e-7,
mass: materials.ship.density*materials.ship.radius*materials.ship.radius*materials.ship.radius,
}
const bigG = 6.674e-20;
const timeScale = 3e6; // 3 miillion seconds per second
function oneStep () {
function calcForces(a, b) {
const dx = Math.max(Math.abs(a.xPos - b.xPos), 1e-20);
const dy = Math.max(Math.abs(a.yPos - b.yPos), 1e-10);
const r2 = (dx*dx+dy*dy)
if (r2 <= a.radius*a.radius+b.radius*b.radius) {
return false;
}
const fg = bigG*a.mass*b.mass/r2;
const theta = Math.atan2(dy, dx);
const fgx = fg*Math.cos(theta);
const fgy = fg*Math.sin(theta);
// acceleration for a
const advx = (-(a.xPos - b.xPos)/dx)*timeScale*fgx;
const advy = (-(a.yPos - b.yPos)/dy)*timeScale*fgy;
a.xVel += advx/a.mass;
a.yVel += advy/a.mass;
// acceleration for b
const bdvx = (-(b.xPos - a.xPos)/dx)*timeScale*fgx;
const bdvy = (-(b.yPos - a.yPos)/dy)*timeScale*fgy;
b.xVel += bdvx/b.mass;
b.yVel += bdvy/b.mass;
if (isNaN(a.xVel) || isNaN(a.yVel)
|| isNaN(b.xVel) || isNaN(b.yVel)) {
console.log(a, b);
planets = [];
}
return true;
}
function updatePos (body, scale=timeScale) {
if (isNaN(body.xPos) || isNaN(body.yPos)) {
console.log(body);
planets = [];
}
body.xPos += scale*body.xVel;
body.yPos += scale*body.yVel;
if (body.xPos < 0) {
body.xVel = Math.max(body.xVel, body.xVel/2);
} else if (body.xPos > sz) {
body.xVel = Math.min(body.xVel, body.xVel/2);
}
if (body.yPos < 0) {
body.yVel = Math.max(body.yVel, body.yVel/2);
} else if (body.yPos > sz) {
body.yVel = Math.min(body.yVel, body.yVel/2);
}
do {
body.xPos = (body.xPos+sz)%sz;
body.yPos = (body.yPos+sz)%sz;
} while (body.xPos < 0 || body.yPos < 0)
}
// update velocities
for (let i = 0; i < planets.length-1; i++) {
const a = planets[i];
// other planets
for (let j = i+1; j < planets.length; j++) {
const b = planets[j];
calcForces(a, b);
}
// ship
calcForces(a, ship)
}
// ship with last planet
calcForces(planets[planets.length-1], ship)
// update positions
for (let i = 0; i < planets.length; i++) {
const a = planets[i];
if (a.material == '???') {
// black holes stay in place, just for fun
continue;
}
updatePos(a);
}
//ship
updatePos(ship)
// clear screen
ctx.fillStyle = '#101010';
ctx.fillRect(0, 0, sz, sz);
// draw planets
for (let i = 0; i < planets.length; i++) {
const p = planets[i];
drawPlanet(p.material, p.xPos, p.yPos, p.radius);
}
// draw ship
drawShip(ship.xPos, ship.yPos);
}
function oneFrame () {
oneStep();
requestAnimationFrame(oneFrame);
}
oneFrame();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment