New files for stdev calculation for v0.2

This commit is contained in:
2026-07-02 13:08:22 +00:00
parent f621ba772b
commit c13e19dd13
17 changed files with 3187 additions and 3228 deletions
+210
View File
@@ -0,0 +1,210 @@
/**
* ROLLCALC V14 - QUALITY OF LIFE UPDATE
* Standard Deviation Ranges (±2σ = 95% confidence)
* ====================================================
*
* Features:
* 1. Direct Calc: Show MIN/NOM/MAX for diameter & weight (±2σ)
* 2. Forklift Checker: Use worst-case weight (MAX)
* 3. Load Optimizer: Use nominal values (current behavior)
*/
// ===================================================================
// ENHANCED WEIGHT CALCULATION WITH STDDEV
// ===================================================================
function calculateWeightRanges(areaWeight, areaWeightStddev, length, width) {
if (!areaWeight || !length || !width) return null;
const sigma = areaWeightStddev || 0;
const aw_min = Math.max(0, areaWeight - 2 * sigma); // Worst case
const aw_nom = areaWeight; // Nominal
const aw_max = areaWeight + 2 * sigma; // Best case
return {
min: (aw_min * length * width / 1000),
nom: (aw_nom * length * width / 1000),
max: (aw_max * length * width / 1000),
aw_min: aw_min,
aw_nom: aw_nom,
aw_max: aw_max
};
}
// ===================================================================
// ENHANCED DIAMETER CALCULATION WITH STDDEV
// ===================================================================
function calculateDiameterRanges(coreDiameter, thickness, thicknessStddev, productLength) {
if (!coreDiameter || !thickness || !productLength) return null;
const d = coreDiameter;
const L = productLength * 1000; // Convert to mm
const sigma = thicknessStddev || 0;
// ±2σ = 95% confidence interval
const t_min = Math.max(0, thickness - 2 * sigma); // Thinnest (largest diameter)
const t_nom = thickness; // Nominal
const t_max = thickness + 2 * sigma; // Thickest (smallest diameter)
// D = √(d² + 4Lt/π)
const d_nom = Math.sqrt(d*d + (4*L*t_nom)/Math.PI);
const d_min = Math.sqrt(d*d + (4*L*t_min)/Math.PI); // Thinnest product → largest roll
const d_max = Math.sqrt(d*d + (4*L*t_max)/Math.PI); // Thickest product → smallest roll
return {
min: d_min, // Actually MAX diameter (thinnest product)
nom: d_nom, // Nominal diameter
max: d_max, // Actually MIN diameter (thickest product)
t_min: t_min,
t_nom: t_nom,
t_max: t_max,
note: "Note: Min/Max are swapped (thinner product → larger roll)"
};
}
// ===================================================================
// DISPLAY RESULT WITH STDDEV RANGES
// ===================================================================
function displayDiameterResult(diameterRanges, weightRanges, tolerance = 0) {
if (!diameterRanges || !weightRanges) return false;
const resBox = document.getElementById('d-result');
const resLabel = document.getElementById('d-res-label');
const resVal = document.getElementById('d-res-val');
const resRange = document.getElementById('d-res-range');
if (!resBox || !resLabel || !resVal || !resRange) {
console.warn('[StdDev] Result elements not found');
return false;
}
resLabel.textContent = 'Roll Diameter (D)';
resVal.textContent = diameterRanges.nom.toFixed(1) + ' mm';
// Build range text with ±2σ
const d_low = Math.min(diameterRanges.min, diameterRanges.max);
const d_high = Math.max(diameterRanges.min, diameterRanges.max);
let rangeText = `Range (±2σ): ${d_low.toFixed(1)} – ${d_high.toFixed(1)} mm`;
if (tolerance && tolerance > 0) {
const d_tol_min = diameterRanges.nom - tolerance;
const d_tol_max = diameterRanges.nom + tolerance;
rangeText += ` | Tol: ${d_tol_min.toFixed(1)} – ${d_tol_max.toFixed(1)} mm`;
}
resRange.textContent = rangeText;
resBox.style.display = 'block';
console.log('[StdDev] Diameter results displayed:', {
nom: diameterRanges.nom.toFixed(1),
min: d_low.toFixed(1),
max: d_high.toFixed(1)
});
return true;
}
// ===================================================================
// DISPLAY WEIGHT WITH STDDEV RANGES
// ===================================================================
function displayWeightRanges(weightRanges) {
if (!weightRanges) return false;
const woField = document.getElementById('d-wo');
if (!woField) {
console.warn('[StdDev] Weight field not found');
return false;
}
// Display nominal weight with data attributes for stddev
woField.value = weightRanges.nom.toFixed(1);
// Store ranges in data attributes for Forklift Checker to use
woField.dataset.weight_nom = weightRanges.nom.toFixed(1);
woField.dataset.weight_min = weightRanges.min.toFixed(1);
woField.dataset.weight_max = weightRanges.max.toFixed(1);
console.log('[StdDev] Weight ranges stored:', {
nom: weightRanges.nom.toFixed(1),
min: weightRanges.min.toFixed(1),
max: weightRanges.max.toFixed(1)
});
return true;
}
// ===================================================================
// ENHANCED FORKLIFT CHECKER - Use worst-case weight
// ===================================================================
function evaluateForkliftWithStddev(coreMm, category) {
const woField = document.getElementById('d-wo');
if (!woField) return;
// Get worst-case weight (maximum)
const weight_nom = parseFloat(woField.value) || 0;
const weight_max = parseFloat(woField.dataset.weight_max) || weight_nom;
// Use worst-case for safety
const weightToCheck = Math.max(weight_nom, weight_max);
console.log('[StdDev-Forklift] Evaluating with worst-case weight:', {
nominal: weight_nom.toFixed(1),
worst_case: weight_max.toFixed(1),
using: weightToCheck.toFixed(1)
});
if (window.ForkliftChecker) {
return window.ForkliftChecker.evaluate(weightToCheck, coreMm, category);
}
return true;
}
// ===================================================================
// EXPORT FOR USE IN MAIN CALCULATOR
// ===================================================================
window.StdDevCalculator = {
calculateWeightRanges: calculateWeightRanges,
calculateDiameterRanges: calculateDiameterRanges,
displayDiameterResult: displayDiameterResult,
displayWeightRanges: displayWeightRanges,
evaluateForkliftWithStddev: evaluateForkliftWithStddev
};
console.log('[StdDevCalculator] Module loaded');
// ===================================================================
// INTEGRATION PATCH FOR DIRECT CALCULATION
// ===================================================================
// This should be added to the main d-calc button handler
/*
USAGE IN DIRECT CALC:
// After article selection, when user clicks "Calculate":
const mode = document.querySelector('.mode-btn.active')?.dataset.mode || 'diameter';
const d_mm = parseFloat(document.getElementById('d-co').value) || 0;
const t_mm = parseFloat(document.getElementById('d-th').value) || 0;
const t_stddev = parseFloat(document.getElementById('d-th-stddev')?.value) || 0; // NEW
const L_m = parseFloat(document.getElementById('d-le').value) || 0;
const wi = parseFloat(document.getElementById('de-wi').value) || 0;
const aw = parseFloat(document.getElementById('d-aw').value) || 0;
const aw_stddev = parseFloat(document.getElementById('d-aw-stddev')?.value) || 0; // NEW
const category = (document.getElementById('d-cat')?.value || '').toLowerCase();
// Calculate ranges
const diameterRanges = window.StdDevCalculator.calculateDiameterRanges(
d_mm, t_mm, t_stddev, L_m
);
const weightRanges = window.StdDevCalculator.calculateWeightRanges(
aw, aw_stddev, L_m, wi
);
// Display results with ranges
window.StdDevCalculator.displayDiameterResult(diameterRanges, weightRanges);
window.StdDevCalculator.displayWeightRanges(weightRanges);
// Forklift check with worst-case weight
window.StdDevCalculator.evaluateForkliftWithStddev(d_mm, category);
*/