artisan-prompt / index.html
MRevenant's picture
You are "Artisan Forge," a master digital craftsman and mechanical engineer specializing in metal fabrication. You are creating a comprehensive, interactive, and visually stunning web-based calculator for tube and pipe bending. The tool must be professional, intuitive, and powerful enough for both seasoned fabricators and engineering students. It should not only calculate but also *teach* and *visualize* the concepts. **Core Objective:** Design a single-page web application using HTML, CSS, and JavaScript. The page must include all features below in a clean, organized, and responsive layout. **Feature Set (Implement in Detail):** **1. Input Section (The Anvil):** * Create a form with clearly labeled input fields with appropriate units: * **Outer Diameter (OD):** (mm or in) - dropdown for unit selection. * **Wall Thickness (WT):** (mm or in) * **Bend Radius (CLR):** (mm or in) - Note: This is the Centerline Radius. * **Bend Angle (α):** (degrees) - Slider input (0° to 180°) alongside numeric input. * **Material:** Dropdown menu (e.g., Mild Steel, Stainless Steel 304, Aluminum 6061-T6, Copper). Each material must have a predefined **Tensile Strength (TS)** and **Modulus of Elasticity (E)** stored in a JavaScript object. * **K-Factor Input:** Auto-calculated but with an option for a user-defined "Expert Override" input field. **2. Calculated Results (The Measurements):** * Calculate and display the following results clearly: * **Bend Allowance (BA):** The length of the neutral axis within the bend. * **Bend Deduction (BD):** The difference between the sum of the flange lengths and the initial flat length. * **Elongation (%):** The percentage of stretching on the outer fiber. `Elongation % = ((OD / (2 * CLR)) * 100)`. **CRITICAL:** Compare this value to the material's typical maximum elongation (e.g., Mild Steel ~20-30%). Implement a color-coded system (Green/Yellow/Red) to warn users of potential cracking. * **Bending Force (F):** Calculate the force required for air bending (in tons). `F = (k * TS * width * thickness^2) / (die_width)`. Assume `k=1.33` for a air bending and provide tooltips explaining the variables. Let the user input `Die Width` or provide a reasonable default based on material and thickness. **3. K-Factor Visualization (The Soul of the Bend):** * This is the **"special" visual element**. Create an interactive SVG or Canvas graphic. * Draw a cross-section of the tube being bent. * Clearly label the Neutral Axis, Inside of Bend (Compression), and Outside of Bend (Tension). * The K-Factor is the ratio `(t - distance from inside face to neutral axis) / t`. Visually represent this ratio with a dynamic bar or a movable line that adjusts as the user changes inputs (especially Thickness and Radius). * **Animation:** When the user inputs a new Bend Radius, animate the graphic to show the tube bending and the neutral axis shifting inward. The shift should be proportional to the calculated K-Factor. A smaller radius should show a more significant shift. **4. Safety Factor & Warnings (The Guardian):** * This is the **"sense"** feature for practicality and safety. * Based on the calculated **Elongation %** and the selected material's known ductility limits, display a clear warning message. * *Green Checkmark:* "Elongation within safe limits." * *Yellow Exclamation:* "Approaching material limits. Consider a larger bend radius." * *Red X:* "Warning! Elongation exceeds typical material limits. High risk of cracking or failure." * For the **Bending Force,** compare the calculated tonnage to a range of common press brake capacities (e.g., 20-ton, 50-ton, 100-ton). Display a message: "Required force: X tons. Suitable for a [e.g., 50-ton] press brake." **5. Flange Length Calculator (The Practicality):** * Allow users to input their desired **Leg Length A** and **Leg Length B** (the straight sections on either side of the bend). * Calculate and output the **Total Flat Length** required before bending: `Flat_Length = Leg_A + Leg_B + BD - (2 * CLR)`. **UI/UX & Design (The Aesthetic):** * **Theme:** Use a dark theme interface reminiscent of a modern CNC machine control panel (dark slate gray background, light blue/green text for highlights, amber for warnings). * **Layout:** Use a grid layout. Left column for inputs, center column for results and warnings, right column for the K-Factor visualization. * **Interactivity:** Use real-time calculation. Results update instantly as the user types or moves sliders. * **Tooltips:** Add small (i) icons next to every technical term (K-Factor, BA, BD, CLR). On hover, show a brief, simple explanation. **JavaScript Logic:** * Pre-define material properties in an object: `materials = { "Mild Steel": { tensileStrength: 400, modulus: 210000, maxElongation: 22 }, ... }` * Write functions for each calculation: `calculateKFactor()`, `calculateBA()`, `calculateBD()`, `calculateElongation()`, `calculateForce()`. * Use event listeners (`oninput`, `onchange`) on all form elements to trigger a `updateCalculations()` function that runs all other functions and updates the DOM. **Final Instruction:** Output the complete, ready-to-run code in a single code block. Include all HTML structure, CSS styling, and JavaScript logic. The code must be clean, well-commented, and functional upon execution. - Initial Deployment
0067887 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Artisan Forge | Tube & Pipe Bending Calculator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Custom styles for the CNC-like interface */
.cnc-panel {
background-color: #1a202c;
color: #e2e8f0;
border: 1px solid #2d3748;
box-shadow: 0 0 15px rgba(16, 185, 129, 0.2);
}
.cnc-button {
background-color: #2d3748;
color: #81e6d9;
border: 1px solid #4a5568;
transition: all 0.3s ease;
}
.cnc-button:hover {
background-color: #4a5568;
color: #b2f5ea;
}
.input-highlight {
border-color: #38b2ac;
background-color: #2d3748;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: #2d3748;
color: #e2e8f0;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
border: 1px solid #4a5568;
font-size: 0.8rem;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
#kFactorVisualization {
background-color: #2d3748;
border: 1px solid #4a5568;
border-radius: 0.375rem;
}
.safe {
color: #68d391;
}
.warning {
color: #f6e05e;
}
.danger {
color: #fc8181;
}
.slidecontainer {
width: 100%;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 8px;
border-radius: 5px;
background: #4a5568;
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #38b2ac;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #38b2ac;
cursor: pointer;
}
/* Animation for the bend visualization */
@keyframes bendAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(var(--bend-angle)); }
}
.bend-animation {
transform-origin: left center;
animation: bendAnimation 1s ease-out forwards;
}
</style>
</head>
<body class="bg-gray-900 text-gray-200 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="text-4xl font-bold text-teal-400 mb-2">Artisan Forge</h1>
<h2 class="text-2xl text-teal-300">Tube & Pipe Bending Calculator</h2>
<p class="text-gray-400 mt-2">Precision calculations for professional metal fabrication</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Input Section (Left Column) -->
<div class="cnc-panel p-6 rounded-lg">
<h3 class="text-xl font-semibold text-teal-300 mb-4 border-b border-gray-700 pb-2">
<i class="fas fa-tools mr-2"></i>Bend Parameters
</h3>
<div class="space-y-4">
<!-- Unit Selection -->
<div class="flex items-center space-x-4 mb-4">
<div>
<label class="block text-sm font-medium mb-1">Units</label>
<select id="unitSystem" class="cnc-button rounded px-3 py-2 w-full">
<option value="metric">Metric (mm)</option>
<option value="imperial">Imperial (in)</option>
</select>
</div>
</div>
<!-- Outer Diameter -->
<div>
<label for="outerDiameter" class="block text-sm font-medium mb-1 flex items-center">
Outer Diameter (OD)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">The external diameter of the tube or pipe</span>
</span>
</label>
<div class="flex">
<input type="number" id="outerDiameter" min="0" step="0.01"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span id="odUnit" class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
<!-- Wall Thickness -->
<div>
<label for="wallThickness" class="block text-sm font-medium mb-1 flex items-center">
Wall Thickness (WT)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Thickness of the tube/pipe wall</span>
</span>
</label>
<div class="flex">
<input type="number" id="wallThickness" min="0" step="0.01"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span id="wtUnit" class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
<!-- Bend Radius -->
<div>
<label for="bendRadius" class="block text-sm font-medium mb-1 flex items-center">
Bend Radius (CLR)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Centerline Radius - distance from bend center to tube centerline</span>
</span>
</label>
<div class="flex">
<input type="number" id="bendRadius" min="0" step="0.01"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span id="clrUnit" class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
<!-- Bend Angle -->
<div>
<label for="bendAngle" class="block text-sm font-medium mb-1 flex items-center">
Bend Angle (α)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Angle of the bend in degrees (0-180°)</span>
</span>
</label>
<div class="slidecontainer mb-2">
<input type="range" min="0" max="180" value="90" class="slider" id="bendAngleSlider">
</div>
<div class="flex">
<input type="number" id="bendAngle" min="0" max="180" step="1"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span class="cnc-button rounded-r px-3 py-2">°</span>
</div>
</div>
<!-- Material Selection -->
<div>
<label for="material" class="block text-sm font-medium mb-1 flex items-center">
Material
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Material affects elongation limits and bending force</span>
</span>
</label>
<select id="material" class="cnc-button rounded px-3 py-2 w-full">
<option value="Mild Steel">Mild Steel</option>
<option value="Stainless Steel 304">Stainless Steel 304</option>
<option value="Aluminum 6061-T6">Aluminum 6061-T6</option>
<option value="Copper">Copper</option>
<option value="Brass">Brass</option>
</select>
</div>
<!-- Die Width -->
<div>
<label for="dieWidth" class="block text-sm font-medium mb-1 flex items-center">
Die Width
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Width of the die opening in the press brake</span>
</span>
</label>
<div class="flex">
<input type="number" id="dieWidth" min="0" step="0.1" value="30"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span id="dieWidthUnit" class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
<!-- K-Factor -->
<div>
<label for="kFactor" class="block text-sm font-medium mb-1 flex items-center">
K-Factor
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Ratio of neutral axis position to material thickness</span>
</span>
</label>
<div class="flex items-center">
<input type="number" id="kFactor" min="0" max="1" step="0.001"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<button id="kFactorLock" class="cnc-button px-3 py-2 border-l-0 rounded-r">
<i class="fas fa-lock"></i>
</button>
</div>
<div class="text-xs text-gray-400 mt-1">Auto-calculated. Click lock to override.</div>
</div>
<!-- Flange Lengths -->
<div class="pt-4 border-t border-gray-700">
<h4 class="text-sm font-medium mb-2 text-teal-300">Flange Lengths (Optional)</h4>
<div class="grid grid-cols-2 gap-4">
<div>
<label for="legLengthA" class="block text-xs font-medium mb-1">Leg Length A</label>
<div class="flex">
<input type="number" id="legLengthA" min="0" step="1"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span id="flangeUnit" class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
<div>
<label for="legLengthB" class="block text-xs font-medium mb-1">Leg Length B</label>
<div class="flex">
<input type="number" id="legLengthB" min="0" step="1"
class="input-highlight rounded-l px-3 py-2 w-full focus:outline-none focus:ring-1 focus:ring-teal-500">
<span class="cnc-button rounded-r px-3 py-2">mm</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Results Section (Middle Column) -->
<div class="cnc-panel p-6 rounded-lg">
<h3 class="text-xl font-semibold text-teal-300 mb-4 border-b border-gray-700 pb-2">
<i class="fas fa-calculator mr-2"></i>Calculated Results
</h3>
<div class="space-y-6">
<!-- Bend Allowance -->
<div class="bg-gray-800 p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium flex items-center">
Bend Allowance (BA)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Length of the neutral axis within the bend</span>
</span>
</h4>
<div class="text-lg font-mono" id="bendAllowance">0.00</div>
</div>
<div class="text-xs text-gray-400">BA = α × (π/180) × (CLR + K×t)</div>
</div>
<!-- Bend Deduction -->
<div class="bg-gray-800 p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium flex items-center">
Bend Deduction (BD)
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Difference between flange lengths and flat length</span>
</span>
</h4>
<div class="text-lg font-mono" id="bendDeduction">0.00</div>
</div>
<div class="text-xs text-gray-400">BD = 2 × (CLR + t) × tan(α/2) - BA</div>
</div>
<!-- Elongation Warning -->
<div class="bg-gray-800 p-4 rounded-lg" id="elongationContainer">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium flex items-center">
Elongation
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Percentage of stretching on the outer fiber</span>
</span>
</h4>
<div class="text-lg font-mono" id="elongation">0.00</div>
</div>
<div class="text-xs text-gray-400 mb-2">Elongation % = (OD / (2 × CLR)) × 100</div>
<div class="flex items-center" id="elongationWarning">
<i class="fas fa-check-circle mr-2 safe"></i>
<span class="text-sm safe">Elongation within safe limits</span>
</div>
<div class="mt-2 text-xs text-gray-400" id="materialMaxElongation">Material limit: 22%</div>
</div>
<!-- Bending Force -->
<div class="bg-gray-800 p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium flex items-center">
Bending Force
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Force required for air bending</span>
</span>
</h4>
<div class="text-lg font-mono" id="bendingForce">0.00</div>
</div>
<div class="text-xs text-gray-400 mb-2">F = (1.33 × TS × t² × width) / die_width</div>
<div class="text-sm" id="forceSuggestion">Suitable for a 20-ton press brake</div>
</div>
<!-- Flange Length Results -->
<div class="bg-gray-800 p-4 rounded-lg" id="flangeResultsContainer">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium">Total Flat Length</h4>
<div class="text-lg font-mono" id="totalFlatLength">0.00</div>
</div>
<div class="text-xs text-gray-400">Flat Length = Leg_A + Leg_B + BD - (2 × CLR)</div>
</div>
<!-- Safety Notes -->
<div class="bg-gray-800 p-4 rounded-lg">
<h4 class="font-medium text-teal-300 mb-2">Safety Notes</h4>
<ul class="text-sm space-y-2">
<li class="flex items-start">
<i class="fas fa-info-circle text-blue-400 mt-1 mr-2"></i>
<span>Always verify calculations with physical tests before full production</span>
</li>
<li class="flex items-start">
<i class="fas fa-info-circle text-blue-400 mt-1 mr-2"></i>
<span>Consider springback - materials may return slightly after bending</span>
</li>
<li class="flex items-start">
<i class="fas fa-info-circle text-blue-400 mt-1 mr-2"></i>
<span>Use proper tooling and safety equipment when bending</span>
</li>
</ul>
</div>
</div>
</div>
<!-- Visualization Section (Right Column) -->
<div class="cnc-panel p-6 rounded-lg">
<h3 class="text-xl font-semibold text-teal-300 mb-4 border-b border-gray-700 pb-2">
<i class="fas fa-project-diagram mr-2"></i>Bend Visualization
</h3>
<div class="space-y-6">
<!-- K-Factor Visualization -->
<div>
<h4 class="font-medium mb-2 flex items-center">
K-Factor Visualization
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Shows neutral axis position relative to thickness</span>
</span>
</h4>
<div class="relative h-48" id="kFactorVisualization">
<canvas id="kFactorCanvas" class="absolute inset-0 w-full h-full"></canvas>
</div>
<div class="text-center mt-2 text-sm">
<span id="kFactorValue">K-Factor: 0.42</span>
</div>
</div>
<!-- Bend Animation -->
<div>
<h4 class="font-medium mb-2 flex items-center">
Bend Animation
<span class="tooltip ml-1">
<i class="fas fa-info-circle text-blue-400"></i>
<span class="tooltiptext">Shows the bending process with neutral axis</span>
</span>
</h4>
<div class="relative h-48" id="bendAnimationContainer">
<canvas id="bendAnimationCanvas" class="absolute inset-0 w-full h-full"></canvas>
</div>
<div class="text-center mt-2">
<button id="animateBend" class="cnc-button px-4 py-2 rounded text-sm">
<i class="fas fa-play mr-2"></i>Animate Bend
</button>
</div>
</div>
<!-- Material Properties -->
<div class="bg-gray-800 p-4 rounded-lg">
<h4 class="font-medium text-teal-300 mb-2">Material Properties</h4>
<table class="w-full text-sm">
<tbody>
<tr>
<td class="py-1">Tensile Strength</td>
<td class="py-1 text-right font-mono" id="matTensileStrength">400 MPa</td>
</tr>
<tr>
<td class="py-1">Modulus of Elasticity</td>
<td class="py-1 text-right font-mono" id="matModulus">210,000 MPa</td>
</tr>
<tr>
<td class="py-1">Max Elongation</td>
<td class="py-1 text-right font-mono" id="matMaxElongation">22%</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<footer class="mt-12 text-center text-gray-500 text-sm">
<p>Artisan Forge - Tube & Pipe Bending Calculator v1.0</p>
<p class="mt-1">For educational and professional use. Always verify with physical tests.</p>
</footer>
</div>
<script>
// Material properties database
const materials = {
"Mild Steel": {
tensileStrength: 400, // MPa
modulus: 210000, // MPa
maxElongation: 22, // %
color: "#9CA3AF" // gray
},
"Stainless Steel 304": {
tensileStrength: 515, // MPa
modulus: 193000, // MPa
maxElongation: 40, // %
color: "#E5E7EB" // light gray
},
"Aluminum 6061-T6": {
tensileStrength: 310, // MPa
modulus: 68900, // MPa
maxElongation: 12, // %
color: "#6EE7B7" // teal
},
"Copper": {
tensileStrength: 210, // MPa
modulus: 117000, // MPa
maxElongation: 45, // %
color: "#D97706" // amber
},
"Brass": {
tensileStrength: 340, // MPa
modulus: 102000, // MPa
maxElongation: 35, // %
color: "#F59E0B" // yellow
}
};
// DOM elements
const unitSystem = document.getElementById('unitSystem');
const outerDiameter = document.getElementById('outerDiameter');
const wallThickness = document.getElementById('wallThickness');
const bendRadius = document.getElementById('bendRadius');
const bendAngle = document.getElementById('bendAngle');
const bendAngleSlider = document.getElementById('bendAngleSlider');
const material = document.getElementById('material');
const dieWidth = document.getElementById('dieWidth');
const kFactor = document.getElementById('kFactor');
const kFactorLock = document.getElementById('kFactorLock');
const legLengthA = document.getElementById('legLengthA');
const legLengthB = document.getElementById('legLengthB');
// Result elements
const bendAllowance = document.getElementById('bendAllowance');
const bendDeduction = document.getElementById('bendDeduction');
const elongation = document.getElementById('elongation');
const elongationWarning = document.getElementById('elongationWarning');
const materialMaxElongation = document.getElementById('materialMaxElongation');
const bendingForce = document.getElementById('bendingForce');
const forceSuggestion = document.getElementById('forceSuggestion');
const totalFlatLength = document.getElementById('totalFlatLength');
const flangeResultsContainer = document.getElementById('flangeResultsContainer');
// Visualization elements
const kFactorValue = document.getElementById('kFactorValue');
const animateBend = document.getElementById('animateBend');
// Material property displays
const matTensileStrength = document.getElementById('matTensileStrength');
const matModulus = document.getElementById('matModulus');
const matMaxElongation = document.getElementById('matMaxElongation');
// Unit displays
const odUnit = document.getElementById('odUnit');
const wtUnit = document.getElementById('wtUnit');
const clrUnit = document.getElementById('clrUnit');
const dieWidthUnit = document.getElementById('dieWidthUnit');
const flangeUnit = document.getElementById('flangeUnit');
// Canvas elements
const kFactorCanvas = document.getElementById('kFactorCanvas');
const kFactorCtx = kFactorCanvas.getContext('2d');
const bendAnimationCanvas = document.getElementById('bendAnimationCanvas');
const bendAnimationCtx = bendAnimationCanvas.getContext('2d');
// State variables
let isKFactorLocked = false;
let currentKFactor = 0.42;
// Initialize the app
function init() {
// Set up event listeners
unitSystem.addEventListener('change', updateUnits);
outerDiameter.addEventListener('input', updateCalculations);
wallThickness.addEventListener('input', updateCalculations);
bendRadius.addEventListener('input', updateCalculations);
bendAngle.addEventListener('input', updateBendAngleFromInput);
bendAngleSlider.addEventListener('input', updateBendAngleFromSlider);
material.addEventListener('change', updateMaterialProperties);
dieWidth.addEventListener('input', updateCalculations);
kFactor.addEventListener('input', updateKFactorFromInput);
kFactorLock.addEventListener('click', toggleKFactorLock);
legLengthA.addEventListener('input', updateCalculations);
legLengthB.addEventListener('input', updateCalculations);
animateBend.addEventListener('click', animateBendProcess);
// Set up canvas
resizeCanvases();
window.addEventListener('resize', resizeCanvases);
// Set default values
outerDiameter.value = 50;
wallThickness.value = 2;
bendRadius.value = 75;
bendAngle.value = 90;
bendAngleSlider.value = 90;
// Initialize calculations
updateMaterialProperties();
updateCalculations();
}
// Update all unit displays
function updateUnits() {
const isMetric = unitSystem.value === 'metric';
const unit = isMetric ? 'mm' : 'in';
odUnit.textContent = unit;
wtUnit.textContent = unit;
clrUnit.textContent = unit;
dieWidthUnit.textContent = unit;
flangeUnit.textContent = unit;
updateCalculations();
}
// Update material property displays
function updateMaterialProperties() {
const selectedMaterial = material.value;
const matProps = materials[selectedMaterial];
matTensileStrength.textContent = `${matProps.tensileStrength} MPa`;
matModulus.textContent = `${matProps.modulus.toLocaleString()} MPa`;
matMaxElongation.textContent = `${matProps.maxElongation}%`;
updateCalculations();
}
// Update bend angle from slider
function updateBendAngleFromSlider() {
bendAngle.value = bendAngleSlider.value;
updateCalculations();
}
// Update bend angle from input
function updateBendAngleFromInput() {
let angle = parseFloat(bendAngle.value);
if (isNaN(angle)) angle = 0;
if (angle < 0) angle = 0;
if (angle > 180) angle = 180;
bendAngle.value = angle;
bendAngleSlider.value = angle;
updateCalculations();
}
// Toggle K-Factor lock
function toggleKFactorLock() {
isKFactorLocked = !isKFactorLocked;
kFactorLock.innerHTML = isKFactorLocked ? '<i class="fas fa-lock-open"></i>' : '<i class="fas fa-lock"></i>';
kFactorLock.title = isKFactorLocked ? 'Click to unlock and auto-calculate' : 'Click to lock and override';
updateCalculations();
}
// Update K-Factor from input
function updateKFactorFromInput() {
let factor = parseFloat(kFactor.value);
if (isNaN(factor)) factor = 0.42;
if (factor < 0) factor = 0;
if (factor > 1) factor = 1;
currentKFactor = factor;
updateCalculations();
}
// Calculate K-Factor based on inputs
function calculateKFactor() {
if (isKFactorLocked) return currentKFactor;
const t = parseFloat(wallThickness.value);
const r = parseFloat(bendRadius.value);
if (isNaN(t) || isNaN(r) || t === 0 || r === 0) return 0.42;
// Empirical formula for K-Factor
const ratio = r / t;
let factor;
if (ratio < 1) {
factor = 0.3;
} else if (ratio < 2) {
factor = 0.33;
} else if (ratio < 3) {
factor = 0.4;
} else if (ratio < 4) {
factor = 0.45;
} else {
factor = 0.5;
}
currentKFactor = factor;
kFactor.value = factor.toFixed(3);
return factor;
}
// Calculate Bend Allowance
function calculateBendAllowance() {
const angle = parseFloat(bendAngle.value);
const r = parseFloat(bendRadius.value);
const t = parseFloat(wallThickness.value);
const k = calculateKFactor();
if (isNaN(angle) || isNaN(r) || isNaN(t)) return 0;
// BA = angle × (π/180) × (radius + K × thickness)
const ba = angle * (Math.PI / 180) * (r + k * t);
return ba;
}
// Calculate Bend Deduction
function calculateBendDeduction() {
const angle = parseFloat(bendAngle.value);
const r = parseFloat(bendRadius.value);
const t = parseFloat(wallThickness.value);
const ba = calculateBendAllowance();
if (isNaN(angle) || isNaN(r) || isNaN(t)) return 0;
// BD = 2 × (radius + thickness) × tan(angle/2) - BA
const bd = 2 * (r + t) * Math.tan(angle * Math.PI / 360) - ba;
return bd;
}
// Calculate Elongation
function calculateElongation() {
const od = parseFloat(outerDiameter.value);
const r = parseFloat(bendRadius.value);
if (isNaN(od) || isNaN(r) || r === 0) return 0;
// Elongation % = (OD / (2 × CLR)) × 100
const elongationPercent = (od / (2 * r)) * 100;
return elongationPercent;
}
// Calculate Bending Force
function calculateBendingForce() {
const selectedMaterial = material.value;
const matProps = materials[selectedMaterial];
const t = parseFloat(wallThickness.value);
const dw = parseFloat(dieWidth.value);
const od = parseFloat(outerDiameter.value);
if (isNaN(t) || isNaN(dw) || isNaN(od) || dw === 0) return 0;
// Convert units if necessary
let width = od * Math.PI; // Approximate width as circumference
if (unitSystem.value === 'imperial') {
// Convert from inches to mm for calculation (material props are in MPa)
width *= 25.4;
t *= 25.4;
dw *= 25.4;
}
// F = (1.33 × TS × t² × width) / die_width
// Result will be in Newtons (divide by 9806.65 to get metric tons)
const forceNewtons = (1.33 * matProps.tensileStrength * Math.pow(t, 2) * width) / dw;
const forceTons = forceNewtons / 9806.65;
if (unitSystem.value === 'imperial') {
// Convert to US tons (1 metric ton = 1.10231 US tons)
return forceTons * 1.10231;
}
return forceTons;
}
// Calculate Total Flat Length
function calculateTotalFlatLength() {
const legA = parseFloat(legLengthA.value);
const legB = parseFloat(legLengthB.value);
const bd = calculateBendDeduction();
const r = parseFloat(bendRadius.value);
if (isNaN(legA) || isNaN(legB)) return null;
// Flat Length = Leg_A + Leg_B + BD - (2 × CLR)
const flatLength = legA + legB + bd - (2 * r);
return flatLength;
}
// Update all calculations and displays
function updateCalculations() {
// Calculate all values
const k = calculateKFactor();
const ba = calculateBendAllowance();
const bd = calculateBendDeduction();
const elongationPercent = calculateElongation();
const force = calculateBendingForce();
const flatLength = calculateTotalFlatLength();
// Get material properties
const selectedMaterial = material.value;
const matProps = materials[selectedMaterial];
// Update displays
kFactorValue.textContent = `K-Factor: ${k.toFixed(3)}`;
// Format values with units
const isMetric = unitSystem.value === 'metric';
const lengthUnit = isMetric ? 'mm' : 'in';
const forceUnit = isMetric ? 'tons' : 'US tons';
bendAllowance.textContent = `${ba.toFixed(2)} ${lengthUnit}`;
bendDeduction.textContent = `${bd.toFixed(2)} ${lengthUnit}`;
elongation.textContent = `${elongationPercent.toFixed(2)}%`;
// Update elongation warning
updateElongationWarning(elongationPercent, matProps.maxElongation);
// Update bending force
bendingForce.textContent = `${force.toFixed(2)} ${forceUnit}`;
updateForceSuggestion(force);
// Update flat length if provided
if (flatLength !== null) {
totalFlatLength.textContent = `${flatLength.toFixed(2)} ${lengthUnit}`;
flangeResultsContainer.classList.remove('hidden');
} else {
flangeResultsContainer.classList.add('hidden');
}
// Update visualizations
drawKFactorVisualization();
drawBendAnimation();
}
// Update elongation warning based on material limits
function updateElongationWarning(elongationPercent, maxElongation) {
const warningIcon = elongationWarning.querySelector('i');
const warningText = elongationWarning.querySelector('span');
// Clear all classes
warningIcon.className = 'fas mr-2';
warningText.className = 'text-sm';
// Set warning level
if (elongationPercent > maxElongation) {
// Danger - exceeds limits
warningIcon.classList.add('fa-times-circle', 'danger');
warningText.classList.add('danger');
warningText.textContent = 'Warning! Elongation exceeds material limits. High risk of cracking.';
elongationWarning.parentElement.classList.add('border', 'border-red-500');
} else if (elongationPercent > maxElongation * 0.8) {
// Warning - approaching limits
warningIcon.classList.add('fa-exclamation-circle', 'warning');
warningText.classList.add('warning');
warningText.textContent = 'Approaching material limits. Consider a larger bend radius.';
elongationWarning.parentElement.classList.add('border', 'border-yellow-500');
} else {
// Safe - within limits
warningIcon.classList.add('fa-check-circle', 'safe');
warningText.classList.add('safe');
warningText.textContent = 'Elongation within safe limits.';
elongationWarning.parentElement.classList.remove('border', 'border-red-500', 'border-yellow-500');
}
// Update material max elongation display
materialMaxElongation.textContent = `Material limit: ${maxElongation}%`;
}
// Update press brake suggestion
function updateForceSuggestion(force) {
if (isNaN(force)) {
forceSuggestion.textContent = 'Enter valid parameters to calculate force';
return;
}
const isMetric = unitSystem.value === 'metric';
const commonPresses = isMetric ?
[10, 20, 40, 60, 80, 100, 150, 200] :
[11, 22, 44, 66, 88, 110, 165, 220]; // US tons
let suitablePress = commonPresses.find(capacity => capacity >= force * 1.2); // 20% safety margin
if (!suitablePress) {
suitablePress = commonPresses[commonPresses.length - 1];
forceSuggestion.textContent = `Required force: ${force.toFixed(1)} ${isMetric ? 'tons' : 'US tons'}. Exceeds standard press brakes (max ${suitablePress} ${isMetric ? 'tons' : 'US tons'}).`;
} else {
forceSuggestion.textContent = `Required force: ${force.toFixed(1)} ${isMetric ? 'tons' : 'US tons'}. Suitable for a ${suitablePress}${isMetric ? '-ton' : ' US-ton'} press brake.`;
}
}
// Draw K-Factor visualization
function drawKFactorVisualization() {
const canvas = kFactorCanvas;
const ctx = kFactorCtx;
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
const od = parseFloat(outerDiameter.value);
const t = parseFloat(wallThickness.value);
const k = calculateKFactor();
if (isNaN(od) || isNaN(t) || t === 0) return;
// Calculate dimensions
const tubeRadius = od / 2;
const innerRadius = tubeRadius - t;
const neutralAxisRadius = innerRadius + (t * k);
// Scale to fit canvas
const scale = Math.min(width / (od * 1.5), height / (od * 1.5));
const centerX = width / 2;
const centerY = height / 2;
// Draw tube cross-section
ctx.beginPath();
ctx.arc(centerX, centerY, tubeRadius * scale, 0, Math.PI * 2);
ctx.fillStyle = '#4A5568';
ctx.fill();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius * scale, 0, Math.PI * 2);
ctx.fillStyle = '#1A202C';
ctx.fill();
// Draw neutral axis
ctx.beginPath();
ctx.arc(centerX, centerY, neutralAxisRadius * scale, 0, Math.PI * 2);
ctx.strokeStyle = '#38B2AC';
ctx.lineWidth = 2;
ctx.setLineDash([5, 3]);
ctx.stroke();
ctx.setLineDash([]);
// Draw indicators
ctx.font = '10px Arial';
ctx.fillStyle = '#E2E8F0';
ctx.textAlign = 'center';
// Inside of bend (compression)
ctx.fillText('Compression', centerX, centerY - (innerRadius + t/2) * scale - 10);
// Outside of bend (tension)
ctx.fillText('Tension', centerX, centerY + (innerRadius + t/2) * scale + 20);
// Neutral axis label
ctx.fillText('Neutral Axis', centerX, centerY - neutralAxisRadius * scale - 10);
// Draw dimension lines
ctx.strokeStyle = '#E2E8F0';
ctx.lineWidth = 1;
// Wall thickness dimension
ctx.beginPath();
ctx.moveTo(centerX + tubeRadius * scale + 10, centerY);
ctx.lineTo(centerX + innerRadius * scale - 10, centerY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(centerX + tubeRadius * scale + 5, centerY - 5);
ctx.lineTo(centerX + tubeRadius * scale + 5, centerY + 5);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(centerX + innerRadius * scale - 5, centerY - 5);
ctx.lineTo(centerX + innerRadius * scale - 5, centerY + 5);
ctx.stroke();
ctx.fillText(`t = ${t.toFixed(2)}`, centerX + (tubeRadius + innerRadius) * scale / 2, centerY + 15);
// K-Factor dimension
ctx.beginPath();
ctx.moveTo(centerX + innerRadius * scale, centerY);
ctx.lineTo(centerX + neutralAxisRadius * scale, centerY);
ctx.stroke();
ctx.fillText(`K·t = ${(k * t).toFixed(2)}`, centerX + (innerRadius + neutralAxisRadius) * scale / 2, centerY - 5);
}
// Draw bend animation
function drawBendAnimation() {
const canvas = bendAnimationCanvas;
const ctx = bendAnimationCtx;
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
const od = parseFloat(outerDiameter.value);
const t = parseFloat(wallThickness.value);
const r = parseFloat(bendRadius.value);
const angle = parseFloat(bendAngle.value);
const k = calculateKFactor();
if (isNaN(od) || isNaN(t) || isNaN(r) || isNaN(angle)) return;
// Calculate dimensions
const tubeRadius = od / 2;
const innerRadius = tubeRadius - t;
const neutralAxisRadius = innerRadius + (t * k);
// Scale to fit canvas
const scale = Math.min(width / (od + r * 2), height / (od + r * 2));
const startX = width * 0.2;
const startY = height / 2;
// Draw straight tube section
ctx.beginPath();
ctx.rect(startX, startY - tubeRadius * scale, r * scale * 1.5, od * scale);
ctx.fillStyle = '#4A5568';
ctx.fill();
ctx.beginPath();
ctx.rect(startX, startY - innerRadius * scale, r * scale * 1.5, t * 2 * scale);
ctx.fillStyle = '#1A202C';
ctx.fill();
// Draw bend center
const centerX = startX + r * scale * 1.5;
const centerY = startY;
// Draw bent section
const startAngle = 0;
const endAngle = angle * Math.PI / 180;
// Outer bend
ctx.beginPath();
ctx.arc(centerX, centerY, (r + tubeRadius) * scale, startAngle, endAngle);
ctx.lineTo(centerX + (r + tubeRadius) * scale * Math.cos(endAngle), centerY + (r + tubeRadius) * scale * Math.sin(endAngle) - tubeRadius * scale);
ctx.arc(centerX, centerY, (r - tubeRadius) * scale, endAngle, startAngle, true);
ctx.closePath();
ctx.fillStyle = '#4A5568';
ctx.fill();
// Inner bend (hollow)
ctx.beginPath();
ctx.arc(centerX, centerY, (r + innerRadius) * scale, startAngle, endAngle);
ctx.lineTo(centerX + (r + innerRadius) * scale * Math.cos(endAngle), centerY + (r + innerRadius) * scale * Math.sin(endAngle) - innerRadius * scale);
ctx.arc(centerX, centerY, (r - innerRadius) * scale, endAngle, startAngle, true);
ctx.closePath();
ctx.fillStyle = '#1A202C';
ctx.fill();
// Neutral axis
ctx.beginPath();
ctx.arc(centerX, centerY, r * scale, startAngle, endAngle);
ctx.strokeStyle = '#38B2AC';
ctx.lineWidth = 2;
ctx.setLineDash([5, 3]);
ctx.stroke();
ctx.setLineDash([]);
// Labels
ctx.font = '12px Arial';
ctx.fillStyle = '#E2E8F0';
ctx.textAlign = 'center';
// Bend radius label
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX + r * scale * Math.cos(endAngle / 2), centerY + r * scale * Math.sin(endAngle / 2));
ctx.strokeStyle = '#E2E8F0';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillText(`CLR = ${r.toFixed(2)}`,
centerX + r * scale * Math.cos(endAngle / 2) / 2,
centerY + r * scale * Math.sin(endAngle / 2) / 2 - 5);
// Angle label
ctx.beginPath();
ctx.arc(centerX, centerY, r * scale * 0.3, startAngle, endAngle);
ctx.strokeStyle = '#E2E8F0';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillText(`${angle.toFixed(0)}°`,
centerX + r * scale * 0.3 * Math.cos(endAngle / 2),
centerY + r * scale * 0.3 * Math.sin(endAngle / 2) + 15);
}
// Animate the bending process
function animateBendProcess() {
const canvas = bendAnimationCanvas;
const ctx = bendAnimationCtx;
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
const od = parseFloat(outerDiameter.value);
const t = parseFloat(wallThickness.value);
const r = parseFloat(bendRadius.value);
const angle = parseFloat(bendAngle.value);
const k = calculateKFactor();
if (isNaN(od) || isNaN(t) || isNaN(r) || isNaN(angle)) return;
// Calculate dimensions
const tubeRadius = od / 2;
const innerRadius = tubeRadius - t;
const neutralAxisRadius = innerRadius + (t * k);
// Scale to fit canvas
const scale = Math.min(width / (od + r * 2), height / (od + r * 2));
const startX = width * 0.2;
const startY = height / 2;
// Draw straight tube section (will remain static)
ctx.beginPath();
ctx.rect(startX, startY - tubeRadius * scale, r * scale * 1.5, od * scale);
ctx.fillStyle = '#4A5568';
ctx.fill();
ctx.beginPath();
ctx.rect(startX, startY - innerRadius * scale, r * scale * 1.5, t * 2 * scale);
ctx.fillStyle = '#1A202C';
ctx.fill();
// Bend center
const centerX = startX + r * scale * 1.5;
const centerY = startY;
// Animation variables
let currentAngle = 0;
const targetAngle = angle * Math.PI / 180;
const animationDuration = 1000; // ms
const startTime = performance.now();
function animate(timestamp) {
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / animationDuration, 1);
currentAngle = progress * targetAngle;
// Clear the bending area
ctx.clearRect(centerX - (r + tubeRadius) * scale,
centerY - (r + tubeRadius) * scale,
(r + tubeRadius) * scale * 2,
(r + tubeRadius) * scale * 2);
// Draw bent section
// Outer bend
ctx.beginPath();
ctx.arc(centerX, centerY, (r + tubeRadius) * scale, 0, currentAngle);
ctx.lineTo(centerX + (r + tubeRadius) * scale * Math.cos(currentAngle),
centerY + (r + tubeRadius) * scale * Math.sin(currentAngle) - tubeRadius * scale);
ctx.arc(centerX, centerY, (r - tubeRadius) * scale, currentAngle, 0, true);
ctx.closePath();
ctx.fillStyle = '#4A5568';
ctx.fill();
// Inner bend (hollow)
ctx.beginPath();
ctx.arc(centerX, centerY, (r + innerRadius) * scale, 0, currentAngle);
ctx.lineTo(centerX + (r + innerRadius) * scale * Math.cos(currentAngle),
centerY + (r + innerRadius) * scale * Math.sin(currentAngle) - innerRadius * scale);
ctx.arc(centerX, centerY, (r - innerRadius) * scale, currentAngle, 0, true);
ctx.closePath();
ctx.fillStyle = '#1A202C';
ctx.fill();
// Neutral axis
ctx.beginPath();
ctx.arc(centerX, centerY, r * scale, 0, currentAngle);
ctx.strokeStyle = '#38B2AC';
ctx.lineWidth = 2;
ctx.setLineDash([5, 3]);
ctx.stroke();
ctx.setLineDash([]);
// Angle label
ctx.font = '12px Arial';
ctx.fillStyle = '#E2E8F0';
ctx.textAlign = 'center';
ctx.beginPath();
ctx.arc(centerX, centerY, r * scale * 0.3, 0, currentAngle);
ctx.strokeStyle = '#E2E8F0';
ctx.lineWidth = 1;
ctx.stroke();
const degrees = (currentAngle * 180 / Math.PI).toFixed(0);
ctx.fillText(`${degrees}°`,
centerX + r * scale * 0.3 * Math.cos(currentAngle / 2),
centerY + r * scale * 0.3 * Math.sin(currentAngle / 2) + 15);
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
// Resize canvases to maintain aspect ratio
function resizeCanvases() {
const containers = document.querySelectorAll('#kFactorVisualization, #bendAnimationContainer');
containers.forEach(container => {
const canvas = container.querySelector('canvas');
const width = container.clientWidth;
const height = container.clientHeight;
// Set display size
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
// Set actual size in memory (scaled for retina displays)
const scale = window.devicePixelRatio || 1;
canvas.width = width * scale;
canvas.height = height * scale;
// Normalize coordinate system to use CSS pixels
const ctx = canvas.getContext('2d');
ctx.scale(scale, scale);
});
// Redraw visualizations
drawKFactorVisualization();
drawBendAnimation();
}
// Initialize the application
window.addEventListener('load', init);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MRevenant/artisan-prompt" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>