A Pen by lilgreenland on CodePen.
Last active
September 11, 2017 03:01
-
-
Save lilgreenland/08f6fc42665cf00e8f5b10d66a2fead3 to your computer and use it in GitHub Desktop.
special relativity: length contraction and time dialation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
<img src="http://lilgreenland.github.io/images/space.jpg"> | |
--> | |
<img src="http://lilgreenland.github.io/images/space.jpg"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var particles = new function() { | |
// canvas setup | |
var canvas = document.createElement("canvas"); | |
var ctx = canvas.getContext("2d"); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
document.body.appendChild(canvas); | |
//resize canvas if window changes | |
window.onresize = function(event) { | |
ctx.canvas.width = window.innerWidth; | |
ctx.canvas.height = window.innerHeight; | |
ctx.lineWidth = settings.strokeWidth; | |
}; | |
var pause = false; | |
// variables | |
var settings = new function() { | |
this.pov = 0; | |
this.showData = false; | |
this.clear = false; | |
this.pause = false; | |
this.time = 0; | |
this.c = 3; | |
this.totalParticles = 1; //window.innerWidth * window.innerHeight / 5000 | |
this.composite = "source-over"; | |
this.alpha = 1; | |
this.gravity = 0; | |
this.acceleration = 0.01; | |
this.observerCycleCounter = 0; | |
this.collision = false; | |
this.restitution = 1; //speed loss on collision with wall or particle | |
this.repulse = 0.016; //controls force of collision (no idea why this needs to be 0.004) | |
this.SpawnSpeed = 0.001; | |
this.fill = true; | |
this.strokeColor = "#ffffff"; | |
this.fillColor = "#333333"; | |
this.pause = function() { | |
if (pause) { | |
pause = false; | |
} else { | |
pause = true; | |
} | |
}; | |
this.clearAll = function() { | |
this.clear = true; | |
}; | |
this.presetShips = function() { | |
pushStar(20, 20, 0); | |
pushStar(20, 150, 0.5 * settings.c); | |
pushStar(20, 300, 0.9 * settings.c); | |
pushStar(20, 450, 0.99 * settings.c); | |
pushStar(20, 600, 0.999 * settings.c); | |
}; | |
this.resetClocks = function() { | |
for (var i = 0; i < stars.length; i++) { | |
stars[i].t = 0 | |
} | |
}; | |
this.cycleObserver = function() { | |
settings.resetClocks(); | |
settings.pov++; | |
if (settings.pov > stars.length - 1) settings.pov = 0; | |
var VxPOV = stars[settings.pov].Vx; | |
for (var i = 0; i < stars.length; i++) { | |
stars[i].x = 20; | |
stars[i].Vx = (stars[i].Vx - VxPOV) / (1 - stars[i].Vx * VxPOV / settings.c / settings.c) | |
} | |
}; | |
} | |
//setup dat.GUI to adjust things inside the function controls | |
var gui2 = new dat.GUI(); | |
gui2.add(settings, 'pause'); | |
gui2.add(settings, 'resetClocks'); | |
gui2.add(settings, 'clearAll'); | |
//gui2.add(settings, 'pov', 0, 10).step(1); | |
gui2.add(settings, 'cycleObserver', 0, 10); | |
var gui = new dat.GUI(); | |
gui.add(settings, 'showData'); | |
// var c_controller = gui.add(settings, 'c', 0.1, 10); | |
// c_controller.onChange(function(value) { | |
// //cap speeds | |
// for (var i = 0; i < stars.length; i++) { | |
// if (stars[i].Vx > settings.c) { | |
// stars[i].Vx = settings.c; | |
// } else if (stars[i].Vx < -settings.c) { | |
// stars[i].Vx = -settings.c; | |
// } | |
// } | |
// }); | |
gui.add(settings, 'acceleration', -0.02, 0.02); | |
gui.add(settings, 'SpawnSpeed', 0, 0.9999); | |
//sets variables back to whole numbers at start | |
//they were set to decimal so dat GUI knew the right stepping on the display | |
settings.acceleration = 0; | |
settings.SpawnSpeed = 0; | |
//updates datGUI | |
for (var i in gui.__controllers) { | |
gui.__controllers[i].updateDisplay(); | |
} | |
for (var i in gui2.__controllers) { | |
gui2.__controllers[i].updateDisplay(); | |
} | |
var stars = []; | |
function pushStar(x, y, Vx) { | |
stars.push({ | |
t: 0, | |
x: x, // + 20 * (Math.random() - 0.5), | |
y: y, // + 20 * (Math.random() - 0.5), | |
Vx: Vx, | |
Rest_Length: 200, | |
Lx: 200, //x length | |
gamma: 0, | |
color: settings.fillColor //'#' + Math.floor(Math.random() * 16777216).toString(16), //random color | |
}); | |
} | |
//starts off with one star at rest | |
settings.presetShips(); | |
//pushStar(canvas.width * 0.5, canvas.height * 0.5, 0); | |
//spawns stars at the start | |
/* for (var i = 0; i < settings.totalParticles; i++) { | |
pushStar(canvas.width * Math.random(), canvas.height * Math.random(), | |
2, | |
0); | |
} */ | |
var mousePos = { | |
x: 0, | |
y: 0 | |
}; | |
//gets mouse position | |
function getMousePos(canvas, evt) { | |
var rect = canvas.getBoundingClientRect(); | |
return { | |
x: evt.clientX - rect.left, | |
y: evt.clientY - rect.top | |
}; | |
} | |
// waits for mouse move and then updates position | |
canvas.addEventListener('mousemove', function(evt) { | |
mousePos = getMousePos(canvas, evt); | |
}, false); | |
//onclick add a new star | |
canvas.addEventListener("mousedown", function() { | |
pushStar(mousePos.x, mousePos.y, settings.SpawnSpeed * settings.c); | |
}); | |
function wallWrap(i) { | |
if (stars[i].x - 30 > canvas.width) { | |
stars[i].x = -200 / stars[i].gamma; | |
} else if (stars[i].x < -200 / stars[i].gamma) { | |
stars[i].x = canvas.width + 30; | |
} | |
} | |
function moveStar(i) { | |
stars[i].gamma = 1 / (Math.sqrt(1 - stars[i].Vx * stars[i].Vx / (settings.c * settings.c))); | |
stars[i].t += 1 / stars[i].gamma; | |
stars[i].x += stars[i].Vx; | |
//acceleration is divided by gamma once for time dialation and once for length contraction. | |
stars[i].Vx += settings.acceleration / stars[i].gamma / stars[i].gamma; | |
} | |
function textOutput(i) { | |
if (settings.showData) { | |
ctx.fillStyle = settings.strokeColor; | |
ctx.font = "20px Arial"; | |
settings.zoom | |
ctx.textAlign = "right"; | |
ctx.fillText('Ɣ=' + stars[i].gamma.toFixed(7).substring(0, 7), stars[i].x - 5, stars[i].y + 45); | |
ctx.fillText('v=' + Math.abs(stars[i].Vx).toFixed(7).substring(0, 7), stars[i].x - 5, stars[i].y + 75); //ctx.fillText('v/c='+Math.abs(stars[i].Vx/settings.c).toFixed(4).substring(0, 4), stars[i].x+5, stars[i].y + 100); | |
} | |
} | |
function drawRocket(i) { | |
if (isFinite(stars[i].gamma)) { | |
var scale = 1 / stars[i].gamma; | |
ctx.save(); | |
//move canvas to the ship and compress in the horizontal direction | |
ctx.translate(stars[i].x, stars[i].y); | |
ctx.scale(scale, 1); | |
//rocket fins | |
if (settings.pov === i) { | |
ctx.fillStyle = '#2E84E6' | |
} else { | |
ctx.fillStyle = '#D90429'; | |
} | |
ctx.beginPath(); | |
ctx.moveTo(20, 10); | |
ctx.lineTo(-20, -10); | |
ctx.quadraticCurveTo(20, -20, 100, 10); | |
ctx.closePath(); | |
ctx.fill(); | |
//lower fin | |
ctx.beginPath(); | |
ctx.moveTo(20, 90); | |
ctx.lineTo(-20, 110); | |
ctx.quadraticCurveTo(20, 120, 100, 90); | |
ctx.closePath(); | |
ctx.fill(); | |
//rocket body | |
ctx.fillStyle = '#EDF2F4'; | |
ctx.beginPath(); | |
ctx.moveTo(0, 10); | |
ctx.quadraticCurveTo(140, -30, 200, 50); | |
ctx.quadraticCurveTo(140, 130, 0, 90); | |
ctx.closePath(); | |
ctx.fill(); | |
//nose | |
if (settings.pov === i) { | |
ctx.fillStyle = '#2E84E6' | |
} else { | |
ctx.fillStyle = '#D90429'; | |
} | |
ctx.beginPath(); | |
ctx.moveTo(180, 28); | |
ctx.quadraticCurveTo(203, 49, 200, 50); | |
ctx.quadraticCurveTo(203, 49, 180, 72); | |
ctx.closePath(); | |
ctx.fill(); | |
// ctx.globalCompositeOperation = 'source-atop'; | |
// if (settings.pov === i) { | |
// ctx.fillStyle = '#2E84E6' | |
// } else { | |
// ctx.fillStyle = '#D90429'; | |
// } | |
// ctx.fillRect(180, 29, 20, 42); | |
// ctx.globalCompositeOperation = 'source-over'; | |
//clock outline | |
ctx.fillStyle = '#D3DBDE'; | |
ctx.beginPath(); | |
ctx.arc(80, 50, 45, 0, 2 * Math.PI * 60); | |
ctx.fill(); | |
//clock | |
//ctx.globalCompositeOperation = 'destination-out' | |
ctx.fillStyle = '#2B2D42'; | |
ctx.beginPath(); | |
ctx.moveTo(80, 50); | |
ctx.arc(80, 50, 45, 0, Math.round((stars[i].t) % (2 * Math.PI * 60)) / 60); | |
ctx.fill(); | |
//undo canvas transformations | |
ctx.restore(); | |
} | |
} | |
function cycle() { | |
if (!pause) { | |
settings.time++ | |
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); | |
for (var i = 0; i < stars.length; i++) { | |
moveStar(i); | |
wallWrap(i); | |
drawRocket(i); | |
textOutput(i); | |
} | |
if (settings.clear) { | |
settings.clear = false | |
stars = [] | |
} | |
} | |
requestAnimationFrame(cycle); | |
} | |
requestAnimationFrame(cycle); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script> |
A Pen by lilgreenland on CodePen.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: #101010; | |
background-image: url("http://lilgreenland.github.io/images/space.jpg"); | |
background-repeat: repeat; | |
overflow: hidden; | |
/* cursor: crosshair; */ | |
margin: 0; | |
} | |
canvas { | |
position: absolute; | |
left: 0; | |
top: 0; | |
z-index: 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment