|
const width = window.innerWidth, height = window.innerHeight; |
|
|
|
const orbitDistance = height / 4, |
|
G = 1e-3 * Math.pow(orbitDistance, 3), // Proportional to cube of orbit distance to maintain behavior over different heights |
|
centralMass = 1, |
|
orbitalV = Math.sqrt(G * centralMass / orbitDistance); |
|
|
|
let initialV = orbitalV, numPnts = 5000; |
|
|
|
// Draw scaffold canvas |
|
d3.select('#scaffold') |
|
.attr('width', width) |
|
.attr('height', height) |
|
.attr('viewBox', `${-width/2} ${-height/2} ${width} ${height}`); |
|
|
|
d3.select('#ghost').attr('cy', -orbitDistance); |
|
|
|
simulateTrajectory(orbitalV, numPnts); |
|
|
|
// |
|
|
|
function simulateTrajectory(initV, numTicks) { |
|
const satellite = { |
|
mass: 0, |
|
x: 0, |
|
y: -orbitDistance, |
|
vx: initV, |
|
vy: 0 |
|
}, |
|
forceSim = d3.forceSimulation() |
|
.alphaDecay(0) |
|
.velocityDecay(0) |
|
.stop() |
|
.force('gravity', d3.forceMagnetic() |
|
.strength(G) |
|
.charge(d => d.mass) |
|
) |
|
.nodes([ |
|
{ mass: centralMass }, |
|
satellite |
|
]); |
|
|
|
// Clear canvas |
|
const ctx = d3.select('canvas#trails') |
|
.attr('width', width) |
|
.attr('height', height) |
|
.node() |
|
.getContext('2d'); |
|
|
|
ctx.translate(width/2, height/2); |
|
ctx.fillStyle = 'rgba(0, 0, 75, .35)'; |
|
|
|
d3.range(numTicks).forEach(() => { |
|
forceSim.tick(); |
|
|
|
ctx.beginPath(); |
|
ctx.fillRect(satellite.x, satellite.y, 1, 1); |
|
ctx.fill(); |
|
}); |
|
|
|
// Animate satellite |
|
const elSatellite = d3.select('#satellite').datum(satellite); |
|
satellite.x = 0; |
|
satellite.y = -orbitDistance; |
|
satellite.vx = initV; |
|
satellite.vy = 0; |
|
|
|
forceSim.restart() |
|
.on('tick', () => { |
|
elSatellite.attr('cx', d => d.x) |
|
.attr('cy', d => d.y); |
|
}); |
|
} |
|
|
|
|
|
// Event handlers |
|
function onVelocityChange(relV) { |
|
d3.select('#velocity-val').text(relV); |
|
initialV = relV * orbitalV; |
|
simulateTrajectory(initialV, numPnts); |
|
} |
|
|
|
function onNumSamplesChange(num) { |
|
d3.select('#samples-val').text(num); |
|
numPnts = num; |
|
simulateTrajectory(initialV, numPnts); |
|
} |