/** * 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); */