Skip to content

Instantly share code, notes, and snippets.

@lilgreenland
Last active September 11, 2017 03:01
Show Gist options
  • Save lilgreenland/08f6fc42665cf00e8f5b10d66a2fead3 to your computer and use it in GitHub Desktop.
Save lilgreenland/08f6fc42665cf00e8f5b10d66a2fead3 to your computer and use it in GitHub Desktop.
special relativity: length contraction and time dialation
<!--
<img src="http://lilgreenland.github.io/images/space.jpg">
-->
<img src="http://lilgreenland.github.io/images/space.jpg">
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);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
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