Skip to content

Instantly share code, notes, and snippets.

@routevegetable
Created May 14, 2016 22:57
Show Gist options
  • Save routevegetable/77b4843a57963a4ddcb7b4f7a9c02c1a to your computer and use it in GitHub Desktop.
Save routevegetable/77b4843a57963a4ddcb7b4f7a9c02c1a to your computer and use it in GitHub Desktop.
Old Javascript wave simulation
<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