diff --git a/public/js/calculators.js b/public/js/calculators.js index b14c735..01b28b9 100644 --- a/public/js/calculators.js +++ b/public/js/calculators.js @@ -5,7 +5,7 @@ import { calculateBPAssessment, computeBPPercentile, loadBpReferenceData } from import { calculateBsa, calculateWeightBasedDose } from './calculators/dosing.js'; import { loadEquipmentData, renderEquipmentResult } from './calculators/equipment.js'; import { calculateGcs } from './calculators/gcs.js'; -import { loadGrowthLmsData } from './calculators/growth.js'; +import { calcMidParentalHeight, calcZFromLMS, interpolateLMS, loadGrowthLmsData, normalCDF, renderGrowthResult, valueFromLMS } from './calculators/growth.js'; import { initImageLightbox } from './calculators/lightbox.js'; import { loadResusMedsData, renderResusMeds } from './calculators/resus.js'; import { loadVitalsData, renderVitalsResult } from './calculators/vitals.js'; @@ -194,25 +194,10 @@ initImageLightbox(); // CDC BMI-for-age LMS values load from /data/calculators/bmi-lms.json. - function normalCDF(z) { - var a1=0.254829592, a2=-0.284496736, a3=1.421413741, a4=-1.453152027, a5=1.061405429, p=0.3275911; - var sign = z < 0 ? -1 : 1; - z = Math.abs(z) / Math.sqrt(2); - var t = 1 / (1 + p * z); - var y = 1 - (((((a5*t+a4)*t)+a3)*t+a2)*t+a1)*t*Math.exp(-z*z); - return 0.5 * (1 + sign * y); - } - // ═══════════════════════════════════════════════════════════ // CHART.JS GROWTH CURVE RENDERING // ═══════════════════════════════════════════════════════════ - // Inverse LMS: given Z-score, return measurement value - function valueFromLMS(L, M, S, z) { - if (Math.abs(L) < 0.001) return M * Math.exp(S * z); - return M * Math.pow(1 + L * S * z, 1 / L); - } - // 7-line reference set: 3, 10, 25, 50, 75, 90, 97. // Drops 5th and 95th (they crowded the 3rd/10th and 90th/97th labels // on mobile without adding clinical value — 3rd/97th are the @@ -431,13 +416,6 @@ initImageLightbox(); }); } - // Mid-parental height calculation - function calcMidParentalHeight(motherHt, fatherHt, sex) { - if (!motherHt || !fatherHt) return null; - var mph = sex === 'male' ? (motherHt + fatherHt + 13) / 2 : (motherHt + fatherHt - 13) / 2; - return { target: mph, low: mph - 8.5, high: mph + 8.5 }; - } - var BMI_LMS = null; loadBmiLmsData() .then(function(data) { BMI_LMS = data; }) @@ -620,42 +598,6 @@ initImageLightbox(); }); }); - function interpolateLMS(lmsTable, val) { - var keys = Object.keys(lmsTable).map(Number).sort(function(a,b){return a-b;}); - if (val <= keys[0]) return lmsTable[keys[0]]; - if (val >= keys[keys.length-1]) return lmsTable[keys[keys.length-1]]; - for (var i = 0; i < keys.length - 1; i++) { - if (val >= keys[i] && val <= keys[i+1]) { - var t = (val - keys[i]) / (keys[i+1] - keys[i]); - var a = lmsTable[keys[i]], b = lmsTable[keys[i+1]]; - return { L: a.L + t*(b.L-a.L), M: a.M + t*(b.M-a.M), S: a.S + t*(b.S-a.S) }; - } - } - return lmsTable[keys[0]]; - } - - function calcZFromLMS(value, L, M, S) { - if (Math.abs(L) < 0.001) return Math.log(value / M) / S; - return (Math.pow(value / M, L) - 1) / (L * S); - } - - function renderGrowthResult(label, value, unit, z, percentile) { - var pctl = (Math.round(percentile * 100) / 100).toFixed(2); - var cl; - if (pctl >= 95 || pctl <= 5) cl = { color: '#f59e0b', bg: '#fef3c7', text: pctl <= 5 ? 'Below 5th percentile' : 'Above 95th percentile' }; - else if (pctl >= 85 || pctl <= 15) cl = { color: '#f97316', bg: '#ffedd5', text: pctl + 'th percentile' }; - else cl = { color: '#10b981', bg: '#d1fae5', text: pctl + 'th percentile' }; - - return '