164 lines
5.4 KiB
JavaScript
164 lines
5.4 KiB
JavaScript
/**
|
|
* scenarios.js — Klinik Senaryo Motoru
|
|
* ---------------------------------------------------------------
|
|
* Her senaryo, physiology cekirdeginin PARAMETRELERINE zamanla
|
|
* mudahale eder. Boylece metrikler gercekten degisir; sadece
|
|
* gorsel animasyon degildir.
|
|
*
|
|
* apply(st, dt) her simule adimda cagrilir.
|
|
* enter(st) senaryo baslangicinda, exit(st) bitisinde.
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
(function (root) {
|
|
'use strict';
|
|
|
|
const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
|
|
const lerp = (a, b, t) => a + (b - a) * t;
|
|
|
|
/** Hedef degere yumusak yaklasim. */
|
|
function approach(cur, target, rate, dt) {
|
|
return lerp(cur, target, clamp(rate * dt, 0, 1));
|
|
}
|
|
|
|
const SCENARIOS = [
|
|
{
|
|
id: 'rest',
|
|
name: 'Dinlenme',
|
|
icon: '🛋️',
|
|
desc: 'Bazal metabolizma. Kalp 72 bpm, oksijen dengesi tam.',
|
|
enter(st) {
|
|
st.hrSet = 72; st.respRate = 14; st.vascularTone = 1.0;
|
|
st.o2Load = 1.0; st.nerveConduction = 1.0; st.neuralTone = 1.0;
|
|
},
|
|
apply(st, dt) {
|
|
st.hr = approach(st.hr, 72, 0.6, dt);
|
|
st.respRate = approach(st.respRate, 14, 0.5, dt);
|
|
st.spo2 = clamp(st.spo2 + dt * 0.15, 0, 99.9);
|
|
st.nerveConduction = approach(st.nerveConduction, 1.0, 0.4, dt);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'exercise',
|
|
name: 'Egzersiz',
|
|
icon: '🏃',
|
|
desc: 'Kaslar O2 cekiyor. Kalp hizi ve debisi tavan yapar, damarlar genisler.',
|
|
enter(st) {
|
|
st.hrSet = 150; st.respRate = 32; st.vascularTone = 0.82;
|
|
st.o2Load = 1.05; st.nerveConduction = 1.25; st.neuralTone = 1.5;
|
|
},
|
|
apply(st, dt) {
|
|
st.hr = approach(st.hr, 150, 0.35, dt);
|
|
st.respRate = approach(st.respRate, 32, 0.4, dt);
|
|
st.vascularTone = approach(st.vascularTone, 0.82, 0.5, dt);
|
|
st.o2Load = approach(st.o2Load, 1.05, 0.3, dt);
|
|
st.nerveConduction = approach(st.nerveConduction, 1.25, 0.3, dt);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'stress',
|
|
name: 'Stres / Adrenalin',
|
|
icon: '⚡',
|
|
desc: 'Sempatik ates. Damarlar daralir, kalp hizlanir, tansiyon yukselir.',
|
|
enter(st) {
|
|
st.hrSet = 118; st.respRate = 22; st.vascularTone = 1.35;
|
|
st.o2Load = 1.0; st.nerveConduction = 1.15; st.neuralTone = 2.2;
|
|
},
|
|
apply(st, dt) {
|
|
st.hr = approach(st.hr, 118, 0.5, dt);
|
|
st.respRate = approach(st.respRate, 22, 0.5, dt);
|
|
st.vascularTone = approach(st.vascularTone, 1.35, 0.6, dt);
|
|
st.nerveConduction = approach(st.nerveConduction, 1.15, 0.5, dt);
|
|
st.neuralTone = approach(st.neuralTone, 2.2, 0.6, dt);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'hypoxia',
|
|
name: 'Oksijen Dususu',
|
|
icon: '🫁',
|
|
desc: 'Akciger verimi dustu (yuksek irtifa / tikali hava yolu). SpO2 cokuyor.',
|
|
enter(st) {
|
|
st.o2Load = 0.45; st.hrSet = 108; st.respRate = 26;
|
|
st.vascularTone = 1.1; st.neuralTone = 1.6;
|
|
},
|
|
apply(st, dt) {
|
|
st.o2Load = approach(st.o2Load, 0.45, 0.4, dt);
|
|
st.hr = approach(st.hr, 108, 0.4, dt);
|
|
st.respRate = approach(st.respRate, 26, 0.4, dt);
|
|
// hipoksi geri beslemesi: SpO2 < 90 ise kalp daha da zorlanir
|
|
if (st.spo2 < 90) st.hr = approach(st.hr, 118, 0.3, dt);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'arrhythmia',
|
|
name: 'Ritim Bozuklugu',
|
|
icon: '💔',
|
|
desc: 'Atriyal fibrilasyon simulasyonu. Ritim duzensiz, debi dalgalanir.',
|
|
enter(st) {
|
|
st.hrSet = 132; st.respRate = 18; st.vascularTone = 1.0;
|
|
st.nerveConduction = 0.95;
|
|
},
|
|
apply(st, dt) {
|
|
// Duzensiz ritim: hedef surekli zipluyor (AF benzeri)
|
|
const jitter = Math.sin(st.t * 3.1) * 26 + Math.sin(st.t * 7.7) * 14;
|
|
st.hr = approach(st.hr, clamp(132 + jitter, 70, 190), 1.8, dt);
|
|
st.respRate = approach(st.respRate, 18, 0.5, dt);
|
|
// Etkin debi dusuk — her atim tam dolmaz
|
|
st.sv = clamp(st.sv * 0.82, 30, 130);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'nerve-delay',
|
|
name: 'Sinir Iletim Gecikmesi',
|
|
icon: '🐌',
|
|
desc: 'Miyelin hasari (MS benzeri). Sinir hizi duser, tepki suresi uzar.',
|
|
enter(st) {
|
|
st.hrSet = 78; st.respRate = 15; st.nerveConduction = 0.35;
|
|
st.vascularTone = 1.0;
|
|
},
|
|
apply(st, dt) {
|
|
st.hr = approach(st.hr, 78, 0.5, dt);
|
|
st.respRate = approach(st.respRate, 15, 0.5, dt);
|
|
st.nerveConduction = approach(st.nerveConduction, 0.35, 0.4, dt);
|
|
st.neuralTone = approach(st.neuralTone, 0.85, 0.5, dt);
|
|
}
|
|
}
|
|
];
|
|
|
|
function getById(id) {
|
|
return SCENARIOS.find(s => s.id === id) || SCENARIOS[0];
|
|
}
|
|
|
|
/** Senaryoyu uygula (her frame cagrilir). */
|
|
function applyScenario(st, dt) {
|
|
const sc = getById(st.scenario);
|
|
sc.apply(st, dt);
|
|
st.hrSet = Math.round(st.hr);
|
|
return sc;
|
|
}
|
|
|
|
/** Senaryo degistir; gecis olayini logla. */
|
|
function setScenario(st, id, physio) {
|
|
const prev = getById(st.scenario);
|
|
const next = getById(id);
|
|
if (prev.id === next.id) return next;
|
|
|
|
st.scenario = id;
|
|
st.scenarioT = 0;
|
|
if (next.enter) next.enter(st);
|
|
|
|
if (physio) {
|
|
physio.logEvent(st, 'info', `Senaryo: ${prev.name} → ${next.name}`);
|
|
}
|
|
return next;
|
|
}
|
|
|
|
const api = { SCENARIOS, getById, applyScenario, setScenario };
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
root.NumexScenarios = api;
|
|
})(typeof window !== 'undefined' ? window : globalThis);
|