Created
February 12, 2019 17:15
-
-
Save andrewluetgers/9bc229d9f55de72a524eab4a9a0da290 to your computer and use it in GitHub Desktop.
Reel to reel demo
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> | |
<body> | |
<script> | |
function round(n) { | |
return Math.round(n * 100)/100 | |
} | |
// parameters for 35mm microfilm rolls | |
let thickness = .147, // 0.110 to 0.180 millimeter https://photo.stackexchange.com/questions/88821/what-is-a-film-base | |
hubDiameter = 31.369, // 1.235 inches | |
//spoolDiameter = 81.788, // 100 feet = 81.788mm | |
spoolDiameter = 81.8760, // 100 feet = 81.788mm | |
spooledLength = 100 * 304.8; | |
// approximate spool math | |
// based on http://www.giangrandi.ch/soft/spiral/spiral.shtml | |
/* @param th number - thickness of material | |
* @param hd number - hub diameter | |
* @param sd number - spool diameter | |
*/ | |
function length(th, hd, sd) { | |
let rot = (sd - hd) / (2 * th), | |
len = Math.PI * rot * (hd + th * (rot - 1)), | |
sa = (rot % 1) * 360; | |
return { | |
rotations: round(rot), | |
angle: round(sa), | |
length: round(len) | |
}; | |
} | |
/* @param th number - thickness of material | |
* @param hd number - hub diameter | |
* @param len number - length of spooled material | |
*/ | |
function diameter(th, hd, len) { | |
let rot = (th - hd + Math.sqrt((hd - th) * (hd - th) + (4 * th * len) / Math.PI)) / (2 * th), | |
sd = 2 * rot * th + hd, | |
sa = (rot % 1) * 360; | |
return { | |
rotations: round(rot), | |
angle: round(sa), | |
diameter: round(sd) | |
} | |
} | |
let calcDia = diameter(thickness, hubDiameter, spooledLength), | |
calcLen = length(thickness, hubDiameter, spoolDiameter); | |
console.log("diameter: ", calcDia, "vs", spoolDiameter, "input length", spooledLength, "err", calcDia.diameter - spoolDiameter); | |
console.log("length: ", calcLen, "vs", spooledLength, "input diameter", spoolDiameter, "err", calcLen.length - spooledLength); | |
</script> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
} | |
.hub { | |
fill: black; | |
stroke-width: 2px; | |
stroke: white; | |
} | |
.spool { | |
fill: #543e2a; | |
} | |
.shaft, | |
.shaft2 { | |
fill: white; | |
} | |
#reel2reel { | |
position: relative; | |
background: slategrey; | |
width: 400px; | |
height: 300px; | |
} | |
#rpms1, | |
#rpms2, | |
#spooled, | |
#time { | |
display: inline-block; | |
text-align: right; | |
position: absolute; | |
top: 200px; | |
left: 50px; | |
width: 80px; | |
} | |
#rpms2 { | |
left: 250px; | |
} | |
#spooled { | |
left: 150px; | |
} | |
#time { | |
top: 220px; | |
left: 150px; | |
} | |
</style> | |
<div id="reel2reel"> | |
<svg width="400" height="300"> | |
<g id="reel1" class="reel" transform="rotate(135 100 100)"> | |
<circle id="s1" class="spool" r="40.93" cx="100" cy="100"></circle> | |
<circle id="h1" class="hub" r="14.68" cx="100" cy="100"></circle> | |
<rect class="shaft" height="8.128" width="8.128" x="95.936" y="95.936" ></rect> | |
<rect class="shaft2" height="12" width="4" x="98" y="98" transform="rotate(45 100 100)"></rect> | |
</g> | |
<g id="reel2" class="reel" transform="rotate(135 300 100)"> | |
<circle id="s2" class="spool" r="40.93" cx="300" cy="100"></circle> | |
<circle id="h2" class="hub" r="14.68" cx="300" cy="100"></circle> | |
<rect class="shaft" height="8.128" width="8.128" x="295.936" y="95.936" ></rect> | |
<rect class="shaft2" height="12" width="4" x="298" y="98" transform="rotate(45 300 100)"></rect> | |
</g> | |
</svg> | |
<span id="rpms1">--</span> | |
<span id="rpms2">--</span> | |
<span id="spooled">0%</span> | |
<span id="time"></span> | |
</div> | |
<script> | |
function spoolFn(selector, x, y) { | |
return function spool(diameter, angle) { | |
document.querySelector(selector).setAttribute("transform", "rotate("+angle+" "+x+" "+y+")"); | |
document.querySelector(selector + " .spool").setAttribute("r", diameter/2); | |
} | |
} | |
let spool1 = spoolFn("#reel1", 100, 100), | |
spool2 = spoolFn("#reel2", 300, 100); | |
function updateSpools(spooledDecimal, total) { | |
let calc1 = diameter(thickness, hubDiameter, total * (1 - spooledDecimal)), | |
calc2 = diameter(thickness, hubDiameter, total * spooledDecimal); | |
spool1(calc1.diameter, calc1.angle); | |
spool2(calc2.diameter, -calc2.angle); | |
return [calc1, calc2] | |
} | |
let now = ()=> new Date().getTime(), | |
playing = true, | |
spooled = 0, | |
calcTime = now(), | |
prevCalcTime = now(), | |
rpmCalcTimeDelta = 0, | |
startTime = 0, | |
runTime = 0, | |
travel = 0.0001, | |
timeStep = 16, | |
timeStep2 = 1, | |
prevCalcs, | |
calcs, | |
rpms1, | |
rpms2; | |
function render() { | |
if (playing) { | |
startTime = !startTime ? now() : startTime; | |
calcTime = now(); | |
calcs = updateSpools(spooled, spooledLength, rpmCalcTimeDelta); | |
runTime = calcTime - startTime; | |
rpmCalcTimeDelta = calcTime - prevCalcTime; | |
prevCalcs && updateRpms(calcs, prevCalcs, rpmCalcTimeDelta, runTime); | |
requestAnimationFrame(render); | |
} | |
} | |
function driveStep() { | |
return setTimeout(function drive() { | |
spooled = playing ? spooled + travel : spooled; | |
playing && driveStep(); | |
playing = spooled < 1; | |
}, timeStep) | |
} | |
function rpmUpdateStep() { | |
setTimeout(function rpmUpdate() { | |
if (playing) { | |
prevCalcs = calcs; | |
prevCalcTime = calcTime; | |
// start with a small time step then increase it to get a more stable rpm calc | |
if (timeStep2 < 2000) { | |
timeStep2 += timeStep2; | |
} | |
rpmUpdateStep(); | |
} | |
}, timeStep2); | |
} | |
function updateRpms(c, p, timeDelta, runTime) { | |
rpms1 = (c[0].rotations - p[0].rotations) * (60000 / timeDelta); | |
rpms2 = (c[1].rotations - p[1].rotations) * (60000 / timeDelta); | |
document.querySelector("#rpms1").innerText = Math.round(rpms1*10)/10 + " RPM"; | |
document.querySelector("#rpms2").innerText = Math.round(rpms2*10)/10 + " RPM"; | |
document.querySelector("#spooled").innerText = (Math.round(spooled * 10000)/100).toFixed(2) + "%"; | |
document.querySelector("#spooled").innerText = (Math.round(spooled * 10000)/100).toFixed(2) + "%"; | |
document.querySelector("#time").innerText = formatMsDuration(runTime); | |
} | |
function formatMsDuration(ms) { | |
var pad = (n, z = 2) => ('00' + n).slice(-z); | |
return pad((ms%3.6e6)/6e4 | 0) + ':' + pad((ms%6e4)/1000|0) + '.' + pad(ms%1000, 3); | |
} | |
driveStep(); | |
rpmUpdateStep(); | |
render(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment