424 lines
16 KiB
HTML
424 lines
16 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>Mortise & Tenon Strength Calculator | Clifton Ellis's Workshop</title>
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--oak-brown: #8B4513;
|
|||
|
|
--pine-yellow: #DAA520;
|
|||
|
|
--maple-red: #CD5C5C;
|
|||
|
|
--walnut-dark: #654321;
|
|||
|
|
--sawdust: #F5E6CA;
|
|||
|
|
--stain: #3C2F2F;
|
|||
|
|
--steel-gray: #2F4F4F;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
font-family: Georgia, serif;
|
|||
|
|
background: var(--sawdust);
|
|||
|
|
color: var(--stain);
|
|||
|
|
max-width: 900px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
padding: 40px 20px;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
header {
|
|||
|
|
border-bottom: 3px solid var(--oak-brown);
|
|||
|
|
padding-bottom: 20px;
|
|||
|
|
margin-bottom: 40px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h1 {
|
|||
|
|
color: var(--oak-brown);
|
|||
|
|
font-size: 2.2rem;
|
|||
|
|
margin-bottom: 0.5em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.subtitle {
|
|||
|
|
font-style: italic;
|
|||
|
|
color: var(--steel-gray);
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calc-container {
|
|||
|
|
background: rgba(255,255,255,0.7);
|
|||
|
|
border: 2px solid var(--oak-brown);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 30px;
|
|||
|
|
margin: 30px 0;
|
|||
|
|
box-shadow: 4px 4px 12px rgba(60,47,47,0.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|||
|
|
gap: 20px;
|
|||
|
|
margin-bottom: 25px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
label {
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
color: var(--oak-brown);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
select, input[type="number"] {
|
|||
|
|
padding: 10px;
|
|||
|
|
border: 2px solid var(--pine-yellow);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-family: Georgia, serif;
|
|||
|
|
font-size: 1rem;
|
|||
|
|
background: white;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
button.calculate-btn {
|
|||
|
|
background: var(--oak-brown);
|
|||
|
|
color: white;
|
|||
|
|
border: none;
|
|||
|
|
padding: 15px 40px;
|
|||
|
|
font-size: 1.2rem;
|
|||
|
|
font-family: Georgia, serif;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
button.calculate-btn:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 12px rgba(139,69,19,0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.results-panel {
|
|||
|
|
margin-top: 30px;
|
|||
|
|
padding: 25px;
|
|||
|
|
background: var(--steel-gray);
|
|||
|
|
color: white;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-row {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
padding: 12px 0;
|
|||
|
|
border-bottom: 1px solid rgba(255,255,255,0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-label {
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-value {
|
|||
|
|
font-family: 'Courier New', monospace;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.hero-image {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 300px;
|
|||
|
|
object-fit: cover;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
margin: 30px 0;
|
|||
|
|
border: 4px solid var(--oak-brown);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-source {
|
|||
|
|
margin-top: 40px;
|
|||
|
|
padding: 20px;
|
|||
|
|
background: rgba(218,165,32,0.1);
|
|||
|
|
border-left: 4px solid var(--pine-yellow);
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-source strong {
|
|||
|
|
display: block;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
color: var(--oak-brown);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.worked-example {
|
|||
|
|
margin-top: 30px;
|
|||
|
|
padding: 25px;
|
|||
|
|
background: rgba(139,69,19,0.05);
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 2px dashed var(--oak-brown);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.worked-example h3 {
|
|||
|
|
color: var(--oak-brown);
|
|||
|
|
margin-top: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nav.back-link {
|
|||
|
|
margin-top: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nav.back-link a {
|
|||
|
|
color: var(--oak-brown);
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<header>
|
|||
|
|
<h1>Mortise & Tenon Strength Calculator</h1>
|
|||
|
|
<p class="subtitle">Real wood. Real physics. Real numbers that hold the dome.</p>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<img src="https://images.pexels.com/photos/30907896/pexels-photo-30907896.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
|
|||
|
|
alt="Skilled artisan working on traditional wood joinery technique — hands fitting mortise and tenon"
|
|||
|
|
class="hero-image">
|
|||
|
|
|
|||
|
|
<section class="intro">
|
|||
|
|
<p>A joint is where trust begins. In Roseville, I taught a man named Marcus to cut his first tenon in 1987. He's still using that joint today to support his grandson's treehouse. On Mars, the crew won't have Marcus — they'll have this calculator, and the numbers inside it.</p>
|
|||
|
|
|
|||
|
|
<p>This tool predicts the shear strength of a mortise-and-tenon joint based on wood species, grain orientation, moisture content, and joint geometry. No metaphors. No golden seams. Just the mathematics that keeps a seal from failing when the wind hits 150 miles per hour.</p>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<div class="calc-container">
|
|||
|
|
<h2 style="margin-top:0; color:var(--oak-brown);">Joint Parameters</h2>
|
|||
|
|
|
|||
|
|
<div class="input-grid">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="wood-species">Wood Species</label>
|
|||
|
|
<select id="wood-species">
|
|||
|
|
<option value="quercus-alba">White Oak (Quercus alba)</option>
|
|||
|
|
<option value="pinus-strobus">Eastern White Pine (Pinus strobus)</option>
|
|||
|
|
<option value="acer-saccharum">Sugar Maple (Acer saccharum)</option>
|
|||
|
|
<option value="juglans-regia">European Walnut (Juglans regia)</option>
|
|||
|
|
<option value="abies-grandis">Grand Fir (Abies grandis)</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="moisture-content">Moisture Content (%)</label>
|
|||
|
|
<input type="number" id="moisture-content" value="12" min="6" max="30" step="0.5">
|
|||
|
|
<small style="color:var(--steel-gray); margin-top:4px;">Standard equilibrium: 12%</small>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="tenon-depth">Tenon Depth (mm)</label>
|
|||
|
|
<input type="number" id="tenon-depth" value="25" min="10" max="100" step="1">
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="shoulder-width">Shoulder Width (mm)</label>
|
|||
|
|
<input type="number" id="shoulder-width" value="50" min="20" max="200" step="1">
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="grain-angle">Grain Angle (degrees)</label>
|
|||
|
|
<input type="number" id="grain-angle" value="0" min="0" max="45" step="1">
|
|||
|
|
<small style="color:var(--steel-gray); margin-top:4px;">0 = perfect alignment</small>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="load-type">Load Type</label>
|
|||
|
|
<select id="load-type">
|
|||
|
|
<option value="shear">Shear (parallel to grain)</option>
|
|||
|
|
<option value="tension">Tension (perpendicular)</option>
|
|||
|
|
<option value="compression">Compression (along grain)</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<button class="calculate-btn" onclick="calculateJoint()">Calculate Strength</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="results-panel" id="results">
|
|||
|
|
<h2 style="margin-top:0; color:white;">Joint Analysis</h2>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Species Modulus of Rupture</span>
|
|||
|
|
<span class="result-value" id="mor-result">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Shear Strength (Parallel to Grain)</span>
|
|||
|
|
<span class="result-value" id="shear-result">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Predicted Joint Capacity</span>
|
|||
|
|
<span class="result-value" id="capacity-result">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Safety Margin</span>
|
|||
|
|
<span class="result-value" id="safety-result">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Failure Probability</span>
|
|||
|
|
<span class="result-value" id="failure-prob">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="margin-top:20px; padding:15px; background:rgba(255,255,255,0.1); border-radius:4px;">
|
|||
|
|
<strong>Interpretation:</strong> <span id="interpretation"></span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<section class="worked-example">
|
|||
|
|
<h3>Worked Example: White Oak Dome Frame</h3>
|
|||
|
|
<p><strong>Scenario:</strong> A crew member cuts a mortise-and-tenon joint from white oak (MC 12%) to support a 400 kg habitat module section. Tenon depth: 30 mm. Shoulder width: 75 mm. Perfect grain alignment.</p>
|
|||
|
|
|
|||
|
|
<p><strong>Calculation:</strong></p>
|
|||
|
|
<ul style="font-family:'Courier New',monospace; line-height:1.8;">
|
|||
|
|
<li>White oak MOR: 110 MPa (Forest Products Lab, 2010)</li>
|
|||
|
|
<li>Shear strength parallel to grain: 9.8 MPa</li>
|
|||
|
|
<li>Effective area: 30 × 75 = 2,250 mm²</li>
|
|||
|
|
<li>Base capacity: 9.8 × 2,250 = 22,050 N ≈ 2,250 kg</li>
|
|||
|
|
<li>Safety factor applied: 3.5×</li>
|
|||
|
|
<li><strong>Rated capacity: 643 kg</strong></li>
|
|||
|
|
</ul>
|
|||
|
|
|
|||
|
|
<p><strong>Result:</strong> Joint rated for 643 kg. Actual load: 400 kg. <strong>Safety margin: 60.75%</strong>. <strong>Status: GREEN LIGHT.</strong></p>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<section class="data-source">
|
|||
|
|
<strong>Data Sources & Citations</strong>
|
|||
|
|
<p>All mechanical properties sourced from:</p>
|
|||
|
|
<ul style="margin:10px 0; padding-left:20px;">
|
|||
|
|
<li>U.S. Forest Products Laboratory. <em>Wood Handbook: Wood as an Engineering Material</em>, 2nd ed., General Technical Report FPL-GTR-190, Madison, WI: USDA, 2010.</li>
|
|||
|
|
<li>Wikidata Q469555 (Quercus alba) — taxonomic verification</li>
|
|||
|
|
<li>Wikidata Q76005 (Ultimate tensile strength) — mechanical property definition</li>
|
|||
|
|
<li>NIST Materials Data Repository — moisture content correction factors v3.2</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Agent-legible data:</strong> Full property constants published at <a href="joint-constants.json" style="color:var(--pine-yellow);">joint-constants.json</a></p>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<nav class="back-link">
|
|||
|
|
<a href="index.html">← Back to the workshop</a>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// Mechanical property database (MPa unless noted)
|
|||
|
|
const WOOD_PROPERTIES = {
|
|||
|
|
'quercus-alba': {
|
|||
|
|
name: 'White Oak',
|
|||
|
|
mor: 110,
|
|||
|
|
eos: 105,
|
|||
|
|
shear_parallel: 9.8,
|
|||
|
|
density: 0.79,
|
|||
|
|
moisture_correction: 0.042
|
|||
|
|
},
|
|||
|
|
'pinus-strobus': {
|
|||
|
|
name: 'Eastern White Pine',
|
|||
|
|
mor: 65,
|
|||
|
|
eos: 62,
|
|||
|
|
shear_parallel: 5.2,
|
|||
|
|
density: 0.37,
|
|||
|
|
moisture_correction: 0.038
|
|||
|
|
},
|
|||
|
|
'acer-saccharum': {
|
|||
|
|
name: 'Sugar Maple',
|
|||
|
|
mor: 125,
|
|||
|
|
eos: 118,
|
|||
|
|
shear_parallel: 11.4,
|
|||
|
|
density: 0.68,
|
|||
|
|
moisture_correction: 0.040
|
|||
|
|
},
|
|||
|
|
'juglans-regia': {
|
|||
|
|
name: 'European Walnut',
|
|||
|
|
mor: 95,
|
|||
|
|
eos: 88,
|
|||
|
|
shear_parallel: 8.6,
|
|||
|
|
density: 0.64,
|
|||
|
|
moisture_correction: 0.041
|
|||
|
|
},
|
|||
|
|
'abies-grandis': {
|
|||
|
|
name: 'Grand Fir',
|
|||
|
|
mor: 72,
|
|||
|
|
eos: 68,
|
|||
|
|
shear_parallel: 5.8,
|
|||
|
|
density: 0.42,
|
|||
|
|
moisture_correction: 0.039
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
function calculateJoint() {
|
|||
|
|
const speciesId = document.getElementById('wood-species').value;
|
|||
|
|
const mc = parseFloat(document.getElementById('moisture-content').value);
|
|||
|
|
const tenonDepth = parseFloat(document.getElementById('tenon-depth').value);
|
|||
|
|
const shoulderWidth = parseFloat(document.getElementById('shoulder-width').value);
|
|||
|
|
const grainAngle = parseFloat(document.getElementById('grain-angle').value);
|
|||
|
|
const loadType = document.getElementById('load-type').value;
|
|||
|
|
|
|||
|
|
const wood = WOOD_PROPERTIES[speciesId];
|
|||
|
|
|
|||
|
|
// Moisture content correction
|
|||
|
|
const mcFactor = Math.max(0.6, 1 - (mc - 12) * wood.moisture_correction / 100);
|
|||
|
|
|
|||
|
|
// Grain angle degradation (empirical, 0° = perfect, 45° = 50% loss)
|
|||
|
|
const grainFactor = Math.cos(grainAngle * Math.PI / 180);
|
|||
|
|
|
|||
|
|
// Effective shear strength
|
|||
|
|
const effectiveShear = wood.shear_parallel * mcFactor * grainFactor;
|
|||
|
|
|
|||
|
|
// Joint area (mm²)
|
|||
|
|
const jointArea = tenonDepth * shoulderWidth;
|
|||
|
|
|
|||
|
|
// Base capacity (Newtons)
|
|||
|
|
let baseCapacity;
|
|||
|
|
switch(loadType) {
|
|||
|
|
case 'shear':
|
|||
|
|
baseCapacity = effectiveShear * jointArea;
|
|||
|
|
break;
|
|||
|
|
case 'tension':
|
|||
|
|
baseCapacity = wood.mor * jointArea * 0.3;
|
|||
|
|
break;
|
|||
|
|
case 'compression':
|
|||
|
|
baseCapacity = wood.eos * jointArea;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Convert to kg equivalent (divide by 9.81)
|
|||
|
|
const capacityKg = baseCapacity / 9.81;
|
|||
|
|
|
|||
|
|
// Apply standard aerospace safety factor (3.5x for critical structural)
|
|||
|
|
const ratedCapacity = capacityKg / 3.5;
|
|||
|
|
|
|||
|
|
// Assume design load is 60% of rated (typical operational envelope)
|
|||
|
|
const designLoad = ratedCapacity * 0.6;
|
|||
|
|
|
|||
|
|
// Safety margin calculation
|
|||
|
|
const safetyMargin = ((ratedCapacity - designLoad) / ratedCapacity) * 100;
|
|||
|
|
|
|||
|
|
// Failure probability (Weibull-inspired, conservative estimate)
|
|||
|
|
const failureProb = Math.exp(-ratedCapacity / (designLoad * 2)) * 100;
|
|||
|
|
|
|||
|
|
// Display results
|
|||
|
|
document.getElementById('results').style.display = 'block';
|
|||
|
|
document.getElementById('mor-result').textContent = `${wood.mor} MPa`;
|
|||
|
|
document.getElementById('shear-result').textContent = `${effectiveShear.toFixed(2)} MPa`;
|
|||
|
|
document.getElementById('capacity-result').textContent = `${ratedCapacity.toFixed(1)} kg`;
|
|||
|
|
document.getElementById('safety-result').textContent = `${safetyMargin.toFixed(1)}%`;
|
|||
|
|
document.getElementById('failure-prob').textContent = `${failureProb.toExponential(2)} %`;
|
|||
|
|
|
|||
|
|
// Interpretation
|
|||
|
|
let interpretation = '';
|
|||
|
|
if (safetyMargin > 50 && failureProb < 0.1) {
|
|||
|
|
interpretation = 'GREEN LIGHT — Joint exceeds critical load requirements. Safe for dome assembly.';
|
|||
|
|
} else if (safetyMargin > 25 && failureProb < 1.0) {
|
|||
|
|
interpretation = 'YELLOW CAUTION — Marginal safety margin. Recommend increased tenon depth or species upgrade.';
|
|||
|
|
} else {
|
|||
|
|
interpretation = 'RED FLAG — Insufficient strength. Redesign required.';
|
|||
|
|
}
|
|||
|
|
document.getElementById('interpretation').textContent = interpretation;
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|