Created
May 14, 2016 22:57
-
-
Save routevegetable/77b4843a57963a4ddcb7b4f7a9c02c1a to your computer and use it in GitHub Desktop.
Old Javascript wave simulation
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
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"></head><body> | |
<canvas id="mycanvas" width="1000" height="500">Ma Canvas | |
</canvas> | |
<div> | |
Damping factor: <input type="text" id="ampScale" value="0.007"><br> | |
Wave speed: <input type="text" id="speed" value="200"><br> | |
Sine: <input type="checkbox" id="sine" defaultChecked="false"><br> | |
Cell Size (pixels): <input type="text" id="cellSize" value="10"><br> | |
Optimize: <input type="checkbox" id="opt" defaultChecked="false"><br> | |
Reflections: <input type="checkbox" id="findReflections" defaultChecked="false"><br> | |
<input type="button" name="updateButton" onclick="updateValues()" value="Update"> | |
</div> | |
<script> | |
var canvas = document.getElementById("mycanvas") | |
var context = canvas.getContext("2d") | |
var width = 1000 | |
var height = 500 | |
var ampScale = 0.3 | |
var speed = 1 | |
var maxAmp = 100 | |
var sine = false | |
var cellSize = 5 | |
var opt = false | |
var findReflections = false | |
// p1: {x,y} | |
// p2: {x,y} | |
function distance(p1, p2) { | |
var dx = p2.x - p1.x | |
var dy = p2.y - p1.y | |
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)) | |
} | |
// Compute amplitude at a specific point past the wavefront | |
function decay(amp, ampScale, x) { | |
var currentAmp = amp * Math.max(0, 1 - ampScale * x) | |
return (sine ? Math.sin(x/10) : 1) * currentAmp | |
} | |
function ampForTravelled(impulse, travelled, ourDistance, ampScale) { | |
// If the wavefront has passed us | |
if(travelled >= ourDistance) { | |
// Where are we after the wavefront | |
var x = travelled - ourDistance | |
return decay(impulse.amp, ampScale, x) | |
} | |
return 0 | |
} | |
//////////////// Wall transformations (for reflection) | |
function vFlip(point) { | |
return { | |
x: point.x, | |
y: point.height-point.y, | |
width: point.width, | |
height: point.height | |
} | |
} | |
function hFlip(point) { | |
return { | |
x: point.width-point.x, | |
y: point.y, | |
width: point.width, | |
height: point.height | |
} | |
} | |
function rotRight(point) { | |
return { | |
x: point.height - point.y, | |
y: point.x, | |
width: point.height, | |
height: point.width | |
} | |
} | |
function identity(point) { | |
return point | |
} | |
function combine(t1, t2) { | |
return function(point) { return t2(t1(point)); } | |
} | |
var wallFuncs = [ | |
identity, // Bottom | |
vFlip, // Top | |
rotRight, // Left | |
combine(rotRight, vFlip) // Right | |
] | |
function getReflectionDistance(srcY, destY, destX) { | |
var c = srcY / destY | |
var point = (c*destX) / (c+1) | |
return distance({x: 0, y: srcY}, {x: point, y: 0}) + | |
distance({x: point, y: 0}, {x: destX, y: destY}) | |
} | |
function computeWallSpaces(impulse) { | |
impulse.wallSpaces = [] | |
for(var i in wallFuncs) { | |
var wallFunc = wallFuncs[i] | |
impulse.wallSpaces.push(wallFunc( | |
{ | |
x: impulse.x, | |
y: impulse.y, | |
width: width, | |
height: height | |
})) | |
} | |
} | |
//////////////// Standard amplitude-finding function | |
// point: {x, y} | |
// impulses: [{when, amp, x, y}] | |
// speed: float | |
// ampScale: float // currentAmplitude = amplitude * max(0, 1 - ampScale * time) | |
// time: float | |
// => value: float | |
function ampAtPoint(point, impulses, speed, ampScale, time) { | |
var total = 0 | |
for(var i in impulses) { | |
var impulse = impulses[i] | |
var timeAlive = time - impulse.when | |
var travelled = speed * timeAlive | |
var distanceFromOrigin = distance(point, impulse) | |
// Compute direct | |
total = total + ampForTravelled(impulse, travelled, distanceFromOrigin, ampScale) | |
// Compute reflected | |
if(findReflections) { | |
for(var j in wallFuncs) { | |
var wallFunc = wallFuncs[j] | |
// Precomputed | |
var impulse2 = impulse.wallSpaces[j] | |
var point2 = wallFunc({ | |
x: point.x, | |
y: point.y, | |
width: width, | |
height: height | |
}) | |
var reflectionDistance = getReflectionDistance(impulse2.y, point2.y, point2.x - impulse2.x) | |
total = total + ampForTravelled(impulse, travelled, reflectionDistance, ampScale) | |
} | |
} | |
} | |
return total; | |
} | |
//////////////// Optimized amplitude-finding-function-producing function | |
function makeAmpAtPoint(impulses, speed, ampScale, time) { | |
var ampFuncs = [] | |
for(var i in impulses) { | |
var impulse = impulses[i] | |
var timeAlive = time - impulse.when | |
var travelled = speed * timeAlive | |
ampFuncs.push( | |
(function(impulse, travelled) { | |
return function(point) { | |
// For this impulse... | |
var total = 0; | |
var distanceFromOrigin = distance(point, impulse) | |
// Compute direct | |
total = total + ampForTravelled(impulse, travelled, distanceFromOrigin, ampScale) | |
// Compute reflected | |
if(findReflections) { | |
for(var j in wallFuncs) { | |
var wallFunc = wallFuncs[j] | |
// Precomputed | |
var impulse2 = impulse.wallSpaces[j] | |
var point2 = wallFunc({ | |
x: point.x, | |
y: point.y, | |
width: width, | |
height: height | |
}) | |
var reflectionDistance = getReflectionDistance(impulse2.y, point2.y, point2.x - impulse2.x) | |
total = total + ampForTravelled(impulse, travelled, reflectionDistance, ampScale) | |
} | |
} | |
return total | |
} | |
})(impulse, travelled)) | |
} | |
return function(point) { | |
var total = 0 | |
for(var i in ampFuncs) { | |
total = total + ampFuncs[i](point) | |
} | |
return total | |
} | |
} | |
function getStyleForAmp(amp, max) { | |
var ampFrac = amp / max | |
var v = (sine ? ((ampFrac * 128) + 128) : ampFrac * 255) | |
v = Math.floor(v) | |
return 'rgb(' + v + ',' + v + ',' + v + ')' | |
} | |
var t = 0 | |
function updateValues() { | |
ampScale = Number(document.getElementById("ampScale").value) | |
speed = Number(document.getElementById("speed").value) | |
sine = Boolean(document.getElementById("sine").checked) | |
cellSize = Number(document.getElementById("cellSize").value) | |
opt = Boolean(document.getElementById("opt").checked) | |
findReflections = Boolean(document.getElementById("findReflections").checked) | |
t = 0 | |
} | |
updateValues() | |
var impulses = [] | |
function drawFrame(time) { | |
// Background | |
context.fillStyle = 'black' | |
context.fillRect(0,0, width, height) | |
// Optimized-to-crap | |
var myAmpAtPoint; | |
if(opt) { | |
myAmpAtPoint = makeAmpAtPoint(impulses, speed, ampScale, time) | |
} | |
// Grid | |
for(var xPos = 0; xPos < width; xPos = xPos + cellSize) { | |
for(var yPos = 0; yPos < height; yPos = yPos + cellSize) { | |
var point = {x: xPos, y: yPos} | |
var amp | |
if(opt) { | |
amp = myAmpAtPoint(point) | |
} else { | |
amp = ampAtPoint(point, | |
impulses, | |
speed, | |
ampScale, | |
time) | |
} | |
context.fillStyle = getStyleForAmp(amp, maxAmp) | |
context.fillRect(xPos, yPos, cellSize, cellSize) | |
} | |
} | |
} | |
function canvasClick(e) { | |
var cX = e.clientX - canvas.offsetLeft | |
var cY = e.clientY - canvas.offsetTop | |
var impulse = { | |
x: cX, | |
y: cY, | |
when: t, | |
amp: maxAmp/2 | |
} | |
computeWallSpaces(impulse) | |
impulses.push(impulse) | |
if(impulses.length > 4) impulses.shift() | |
} | |
canvas.addEventListener("click", canvasClick, false) | |
setInterval((function() { | |
t = t + 0.1 | |
drawFrame(t) | |
}), 100) | |
</script> | |
</body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment