Last active
December 15, 2019 21:49
-
-
Save LeopoldTal/ceb164a1be41aca6c13e4afae254a671 to your computer and use it in GitHub Desktop.
Making a swimming pool out of cheese
This file contains hidden or 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
const DEFAULT_POOL_DIAMETER = 25; // m | |
const DEFAULT_POOL_DEPTH = 1.5; // m | |
// Cheese tensile strengths in kPa | |
// Sundaram Gunasekaran & M. Mehmet Ak | |
// _Cheese Rheology and Texture_ (2002) | |
// Table 3.2, p. 126 | |
const CHEESES = { | |
'American cheese': 138, | |
'Appenzell': 22, | |
'Camembert, 15 days old': 1050, | |
'Camembert, 22 days old': 251, | |
'Cheddar': 242, | |
'Double Gloucester': 850, | |
'Edam': 290, | |
'Emmental': 182, | |
'Feta': 470, | |
'Gouda': 405, | |
'Gruyère': 77, | |
'Lancashire': 710, | |
'Leicester': 650, | |
'Mahon, fresh': 497, | |
'Mahon, aged': 2487, | |
'Mozzarella': 49, | |
'Münster': 6, | |
'Parmesan': 1980, | |
'Pecorino Romano': 2840, | |
'String cheese': 100 | |
}; | |
// Surface gravities in m/s^2 | |
const GRAVITIES = { | |
Earth: 9.81, | |
Moon: 1.63, | |
Mercury: 3.7, | |
Venus: 8.87, | |
Mars: 3.72, | |
Jupiter: 24.79, | |
Saturn: 10.44, | |
Uranus: 8.69, | |
Neptune: 11.15, | |
Titan: 1.352, | |
Ceres: 0.28, | |
Pluto: 0.620, | |
Eris: 0.82, | |
Haumea: 0.401, | |
Makemake: 0.57, | |
Quaoar: 0.3, | |
Orcus: 0.2, | |
Sun: 274 | |
}; | |
// Densities in kg/L | |
const LIQUIDS = { | |
'water': 1, | |
'mud': 1.84, | |
'dry sand': 1.55, | |
'wet sand': 1.9, | |
'blood': 1.06, | |
'human fat': 0.9, | |
'pure alcohol': 0.785, | |
'wine (12.5°)': 0.973, | |
'rum (40°)': 0.914, | |
'olive oil': 0.8, | |
'molasses': 1.41, | |
'sulphuric acid': 1.84, | |
'bromine': 3.103, | |
'gallium (29.8°C)': 6.095, | |
'mercury': 13.5, | |
// random-packed spheres (packing ratio 62.5%) | |
'glass marbles': 1.6, | |
'ball bearings': 4.91, | |
'eyeballs': 0.625 | |
}; | |
const showNumber = n => (Math.round(100 * n) / 100).toString(); | |
const initPoolDimensions = () => { | |
console.debug('Initialising pool size'); | |
document.getElementById('pool-diameter').value = DEFAULT_POOL_DIAMETER; | |
document.getElementById('pool-depth').value = DEFAULT_POOL_DEPTH; | |
console.debug('Pool size init done.'); | |
} | |
const initCheeseOptions = () => { | |
const cheeseNames = Object.keys(CHEESES); | |
console.debug(`Initialising ${cheeseNames.length} cheese options`); | |
const select = document.getElementById('cheese-type'); | |
cheeseNames.sort(); | |
cheeseNames.forEach(name => { | |
const option = document.createElement('option'); | |
option.value = name; | |
option.innerText = name; | |
select.appendChild(option); | |
}); | |
console.debug('Cheese options init done.'); | |
}; | |
const initLiquidOptions = () => { | |
const liquidNames = Object.keys(LIQUIDS); | |
console.debug(`Initialising ${liquidNames.length} pool contents options`); | |
const select = document.getElementById('liquid-type'); | |
liquidNames.forEach(name => { | |
const option = document.createElement('option'); | |
option.value = name; | |
option.innerText = name; | |
select.appendChild(option); | |
}); | |
console.debug('Pool contents options init done.'); | |
}; | |
const initGravityOptions = () => { | |
const bodyNames = Object.keys(GRAVITIES); | |
console.debug(`Initialising ${bodyNames.length} gravity options`); | |
const select = document.getElementById('celestial-body'); | |
bodyNames.forEach(name => { | |
const option = document.createElement('option'); | |
option.value = name; | |
option.innerText = `${name} (${showNumber(GRAVITIES[name])}\u202fm/s²)`; | |
select.appendChild(option); | |
}); | |
console.debug('Gravity options init done.'); | |
}; | |
const getWallThickness = ({ | |
gravity, | |
density, | |
poolDiameter, | |
poolDepth, | |
tensileStrength | |
}) => ( | |
gravity * density * poolDepth * poolDiameter / 2 / tensileStrength | |
); | |
const draw = (wallThickness, poolDiameter, poolDepth) => { | |
const totalPoolWidth = 2 * wallThickness + poolDiameter; | |
const drawingWidth = document.getElementById('graphic').getBoundingClientRect().width; | |
const getSize = n => `${ | |
Math.floor(drawingWidth * n / totalPoolWidth) | |
}px`; | |
const wallThicknessInPx = getSize(wallThickness); | |
const poolDiameterInPx = getSize(poolDiameter); | |
const poolDepthInPx = getSize(poolDepth); | |
console.debug(`Drawing pool size ${totalPoolWidth}\u202fm on ${ | |
drawingWidth}\u202fpx.`); | |
console.debug(`Walls: ${wallThickness}\u202f×\u202f${poolDepth}\u202fm → ${ | |
wallThicknessInPx}\u202f×\u202f${poolDepthInPx}`); | |
console.debug(`Liquid: ${poolDiameter}\u202f×\u202f${poolDepth}\u202fm → ${ | |
poolDiameterInPx}\u202f×\u202f${poolDepthInPx}`); | |
const walls = document.getElementsByClassName('graphic-wall'); | |
walls[0].style.width = wallThicknessInPx; | |
walls[1].style.width = wallThicknessInPx; | |
walls[0].style.height = poolDepthInPx; | |
walls[1].style.height = poolDepthInPx; | |
const liquid = document.getElementsByClassName('graphic-liquid')[0]; | |
liquid.style.width = poolDiameterInPx; | |
liquid.style.height = poolDepthInPx; | |
}; | |
const compute = () => { | |
console.debug('Computation start'); | |
const poolDiameter = parseFloat(document.getElementById('pool-diameter').value); | |
console.debug(`Input pool diameter: ${poolDiameter}\u202fm.`); | |
const poolDepth = parseFloat(document.getElementById('pool-depth').value); | |
console.debug(`Input pool depth: ${poolDepth}\u202fm.`); | |
const cheeseType = document.getElementById('cheese-type').value; | |
console.debug(`Selected cheese type: ${cheeseType}`); | |
const tensileStrength = CHEESES[cheeseType]; | |
console.debug(`Input tensile strength: ${tensileStrength} kPa`); | |
const liquidType = document.getElementById('liquid-type').value; | |
console.debug(`Selected pool contents: ${liquidType}`); | |
const density = LIQUIDS[liquidType]; | |
console.debug(`Input density: ${density} kg/L`); | |
const celestialBody = document.getElementById('celestial-body').value; | |
console.debug(`Selected celestial body: ${celestialBody}`); | |
const gravity = GRAVITIES[celestialBody]; | |
console.debug(`Input gravity: ${gravity} m/s^2`); | |
const wallThickness = getWallThickness({ | |
gravity, | |
density, | |
poolDiameter, | |
poolDepth, | |
tensileStrength | |
}); | |
console.debug(`Output wall thickness: ${wallThickness} m`); | |
console.debug('Setting text display'); | |
const outEl = document.getElementById('wall-thickness'); | |
outEl.innerText = showNumber(wallThickness); | |
console.debug('Setting graphic display'); | |
draw(wallThickness, poolDiameter, poolDepth); | |
console.debug('Computation done.'); | |
}; | |
const init = () => { | |
console.debug('Start init'); | |
initPoolDimensions(); | |
initCheeseOptions(); | |
initLiquidOptions(); | |
initGravityOptions(); | |
document.getElementById('cheese-pool-form') | |
.addEventListener('change', compute); | |
window.addEventListener('resize', compute); | |
console.debug('Init done.'); | |
}; | |
init(); | |
compute(); |
This file contains hidden or 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Making a swimming pool out of cheese</title> | |
<style> | |
h1 { | |
text-align: center; | |
font-family: sans-serif; | |
} | |
.graphic-wall, | |
.graphic-liquid { | |
display: inline-block; | |
margin: 0; | |
padding: 0; | |
} | |
.graphic-wall { | |
background: orange; | |
} | |
.graphic-liquid { | |
background: turquoise; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Making a swimming pool out of cheese</h1> | |
<noscript>You'll need Javascript to compute your cheese pool.</noscript> | |
<form id="cheese-pool-form" action="#"> | |
<p> | |
Your pool is | |
<label for="pool-diameter"> | |
<input | |
type="number" | |
name="pool-diameter" id="pool-diameter" | |
min="0.5" step="0.5" | |
/> m wide | |
</label> | |
and | |
<label for="pool-depth"> | |
<input | |
type="number" | |
name="pool-depth" id="pool-depth" | |
min="0.1" step="0.1" | |
/> m deep | |
</label>. | |
</p> | |
<p> | |
<label for="cheese-type">The walls of your pool are made out of | |
<select name="cheese-type" id="cheese-type"></select> | |
</label>. | |
</p> | |
<p> | |
<label for="liquid-type">Your pool is full of | |
<select name="liquid-type" id="liquid-type"></select> | |
</label>. | |
</p> | |
<p> | |
<label for="celestial-body">Your pool is in | |
<select name="celestial-body" id="celestial-body"></select> | |
gravity.</label> | |
<small> | |
Note that the properties of cheese may change on other celestial bodies, | |
or in extreme Earth environments. | |
</small> | |
</p> | |
</form> | |
<p> | |
Your pool needs walls <strong id="wall-thickness">???</strong> m thick. | |
</p> | |
<div id="graphic"> | |
<div class="graphic-wall"></div><div class="graphic-liquid"></div><div class="graphic-wall"></div> | |
</div> | |
<script src="./cheese-pool.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment