import 'dart:math' as math; import 'package:fis_measure/configs/cardiac.dart'; import 'package:fis_measure/configs/patient.dart'; import 'package:fis_measure/interfaces/enums/species.dart'; import 'package:fis_measure/process/calcuators/formulas/general.dart'; import 'package:fis_measure/utils/number.dart'; class CardiacFormulas { static ICardiacFormulaStrategy _singleton = AnimalsCardiacFormulas(); static void reinitialize() { if (GlobalPatientConfig.speciesType == SpeciesType.mouse) { GlobalCardiacConfigs.density = 1.05; _singleton = MouseCardiacFormulas(); } else { GlobalCardiacConfigs.density = 1.04; _singleton = AnimalsCardiacFormulas(); } } static double teiIndex(double co, double et) => _singleton.teiIndex(co, et); static double ef(double edv, double esv) => _singleton.ef(edv, esv); static double edvTeichholz(double lvidd) => _singleton.edvTeichholz(lvidd); static double edvCube(double lvidd) => _singleton.edvCube(lvidd); static double esvTeichholz(double lvids) => _singleton.esvTeichholz(lvids); static double esvCube(double lvids) => _singleton.esvCube(lvids); static double sv(double edv, double esv) => _singleton.sv(edv, esv); static double co(double sv, {required int hr}) => _singleton.co(sv, hr: hr); static double ci(double sv, {required int hr, required double bsa}) => _singleton.ci(sv, hr: hr, bsa: bsa); static double lvdMass(double ivsd, double lvidd, double lvpwd) => _singleton.lvdMass(ivsd, lvidd, lvpwd); static double lvdMassAL( double lvadSaxEpi, double lvadSaxEndo, double lvldApical) => _singleton.lvdMassAL(lvadSaxEpi, lvadSaxEndo, lvldApical); static double lvdMassIndex(double lvdmass, double bsa) => _singleton.lvdMassIndex(lvdmass, bsa); static double fsPercent(double lvidd, double lvids) => _singleton.fsPercent(lvidd, lvids); static double ivsPercent(double ivss, double ivsd) => _singleton.ivsPercent(ivss, ivsd); static double lvpwPercent(double lvpws, double lvpwd) => _singleton.lvpwPercent(lvpws, lvpwd); static double mamPercent(double mapse, double lvidd, double lvids) => _singleton.mamPercent(mapse, lvidd, lvids); static double si(double sv, double bsa) => _singleton.si(sv, bsa); static double flowAreaByVTI(double otDiam, double otvti, double vti) => _singleton.flowAreaByVTI(otDiam, otvti, vti); static double dviByVTI(double otvti, double vti) => _singleton.dviByVTI(otvti, vti); static double avaIndex(double avaByVTI, double bsa) => _singleton.avaIndex(avaByVTI, bsa); static double lvevi(double lvev, double bsa) => _singleton.lvevi(lvev, bsa); static double lvSimsonVolume(double l, List? a, List? b, [int num = 20]) => _singleton.lvSimsonVolume(l, a, b, num); static double mrEROA(double mrRadius, double mrAlsVel, double mrVmax) => _singleton.mrEROA(mrRadius, mrAlsVel, mrVmax); static double mrRV(double mrEROA, double mrVti) => _singleton.mrRV(mrEROA, mrVti); static double mrFlowRate(double mrRadius, double mrAlsVel) => _singleton.mrFlowRate(mrRadius, mrAlsVel); static double lvidIndex(double lvid, double bsa) => _singleton.lvidIndex(lvid, bsa); static double lvidN(double lvid, double weight) => _singleton.lvidN(lvid, weight); static double phtByDecT(double dt) => _singleton.phtByDecT(dt); static double mvaByPht(double mvPht) => _singleton.mvaByPht(mvPht); // CardiacFormulas._internal(); // 私有构造函数 static double bsa(double weight) { return 0.1 * math.pow(weight, 0.667); } } abstract class ICardiacFormulaStrategy { double teiIndex(double co, double et); double ef(double edv, double esv); double edvTeichholz(double lvidd); double edvCube(double lvidd); double esvTeichholz(double lvids); double esvCube(double lvids); double sv(double edv, double esv); double co(double sv, {required int hr}); double ci(double sv, {required int hr, required double bsa}); double lvdMass(double ivsd, double lvidd, double lvpwd); double lvdMassAL(double lvadSaxEpi, double lvadSaxEndo, double lvldApical); double lvdMassIndex(double lvdmass, double bsa); double fsPercent(double lvidd, double lvids); double ivsPercent(double ivss, double ivsd); double lvpwPercent(double lvpws, double lvpwd); double mamPercent(double mapse, double lvidd, double lvids); double si(double sv, double bsa); double flowAreaByVTI(double otDiam, double otvti, double vti); double dviByVTI(double otvti, double vti); double avaIndex(double avaByVTI, double bsa); double lvevi(double lvev, double bsa); double lvSimsonVolume(double l, List? a, List? b, [int num = 20]); double mrEROA(double mrRadius, double mrAlsVel, double mrVmax); double mrRV(double mrEROA, double mrVti); double mrFlowRate(double mrRadius, double mrAlsVel); /// LVID Index /// /// [lvid] cm /// /// [bsa] m2 double lvidIndex(double lvid, double bsa); /// LVID N /// /// [lvid] cm /// /// [weight] kg double lvidN(double lvid, double weight); /// /// Pht by DecT = 0.29 * dt /// /// s /// s double phtByDecT(double dt); /// /// MVA by PHT = 22.0/MV PHT/100 cm^2 /// /// s /// (unit cm^2) double mvaByPht(double mvPht); } /// 基础公式 class BaseCardiacFormulas implements ICardiacFormulaStrategy { /// IMP /// /// Formula: `(CO-ET)/ET` /// /// Result Unit: `None` @override double teiIndex(double co, double et) { double imp = 0.0; if (et != 0.0) { imp = (((co).abs() - (et).abs()) / et).abs(); } return imp; } /// EF /// Formula: `(EDV - ESV )/EDV` /// /// [edv] Unit: `cm³` /// /// [esv] Unit: `cm³` /// /// Result Unit: `None` @override double ef(double edv, double esv) { // 这行判断暂时注释掉是为了使实际表现与旧版一致 // if (edv < esv) { // return double.nan; // } return (edv - esv) / edv * 100; } /// EDV (Teichholz) /// /// Formula: `[7.0/(2.4 + LVIDd)] x LVIDd^3` /// /// [lvidd] Unit: `cm` /// /// Result Unit: `cm³` @override double edvTeichholz(double lvidd) { double edv = double.nan; if (!NumUtil.almostEquals(lvidd, 0)) { edv = 7.0 * math.pow(lvidd, 3) / (2.4 + lvidd); } return edv; } /// EDV (Cube) /// /// Formula: `LVIDd^3` /// /// [lvidd] Unit: `cm` /// /// Result Unit: `cm³` @override double edvCube(double lvidd) { double edv = double.nan; if (!NumUtil.almostEquals(lvidd, 0)) { edv = math.pow(lvidd, 3).toDouble(); } return edv; } /// ESV (Teichholz) /// /// Formula: `[7.0/(2.4 + LVIDs)] x LVIDs^3` /// /// [lvids] Unit: `cm` /// /// Result Unit: `cm³` @override double esvTeichholz(double lvids) { // 计算公式相同,入参不同 return edvTeichholz(lvids); } /// ESV (Cube) /// /// Formula: `LVIDs^3` /// /// [lvids] Unit: `cm` /// /// Result Unit: `cm³` @override double esvCube(double lvids) { // 计算公式相同,入参不同 return edvCube(lvids); } /// SV @override double sv(double edv, double esv) { return edv - esv; } /// CO @override double co( double sv, { required int hr, }) { return (sv - hr) / 1000.0; } /// CI @override double ci( double sv, { required int hr, required double bsa, }) { return ((sv - hr) / 1000.0) / bsa; } /// /// LVEVI = LVEV / BSA /// /// Unit cm³ /// Unit m² /// cm³/m² @override double lvevi(double lvev, double bsa) { return lvev / bsa; } /// LVdMass /// LVd Mass(2D) @override double lvdMass(double ivsd, double lvidd, double lvpwd) { final density = GlobalCardiacConfigs.density; double part1 = math.pow(ivsd + lvidd + lvpwd, 3).toDouble(); double part2 = math.pow(lvidd, 3).toDouble(); double value = (density * (part1 - part2)) / 1000.0; return value; } /// LVd Mass AL @override double lvdMassAL( double lvadSaxEpi, double lvadSaxEndo, double lvldApical, ) { double t = math.sqrt(lvadSaxEpi / math.pi) - math.sqrt(lvadSaxEndo / math.pi); double mass = 1.05 * 5 / 6 * (lvadSaxEpi * (lvldApical + t) - lvadSaxEndo * lvldApical) / 1000; return mass; } /// LVd Mass Index @override double lvdMassIndex(double lvdmass, double bsa) { return lvdmass / bsa * 1000; } /// %FS @override double fsPercent(double lvidd, double lvids) { return ((lvidd - lvids) / lvidd) * 100; } /// %IVS @override double ivsPercent(double ivss, double ivsd) { return ((ivss - ivsd) / ivsd) * 100; } /// %LVPW @override double lvpwPercent(double lvpws, double lvpwd) { return ((lvpws - lvpwd) / lvpwd) * 100; } /// MAM% @override double mamPercent(double mapse, double lvidd, double lvids) { return mapse / (lvidd - lvids + mapse) * 100; } /// SI /// /// (EDV - ESV)/BSA /// /// [sv] cm³ /// /// [bsa] m² /// /// return `cm³/m²` @override double si(double sv, double bsa) { double si = sv / bsa; return si; } /// /// MVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/MV VTI /// AVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/AV VTI /// TVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/TV VTI /// PVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/PV VTI /// /// cm /// cm /// cm /// cm^2 @override double flowAreaByVTI(double otDiam, double otvti, double vti) { double sv = 0.25 * math.pi * math.pow(otDiam, 2) * otvti / vti; return sv; } /// cm /// cm /// Unit None @override double dviByVTI(double otvti, double vti) { double dvi = double.nan; if (!GeneralFormulas.doubleAlmostEquals(vti, 0)) { dvi = otvti / vti; } return dvi; } /// /// AVA Index = avaByVTI/bsa /// /// cm2 /// m2 /// cm2/m2 @override double avaIndex(double avaByVTI, double bsa) { double index = double.nan; if (!GeneralFormulas.doubleAlmostEquals(bsa, 0)) { index = avaByVTI / bsa; } return index; } /// /// Formular : V= π/4×∑_(i=1)^20▒〖(a_i×b_i)〗×L/20 /// /// @override double lvSimsonVolume(double l, List? a, List? b, [int num = 20]) { double volume = double.nan; if (a != null && b != null) { double sum = 0; for (int i = 0; i < num; i++) { sum += a[i] * b[i] * l / num; } volume = math.pi / 4 * sum; } return volume; } @override double mrEROA(double mrRadius, double mrAlsVel, double mrVmax) { double mrEROA = double.nan; if (!GeneralFormulas.doubleAlmostEquals(mrVmax, 0)) { mrEROA = 2 * math.pi * math.pow(mrRadius, 2) * mrAlsVel / (mrVmax).abs(); } return mrEROA; } @override double mrRV(double mrEROA, double mrVti) { return mrEROA * mrVti; } @override double mrFlowRate(double mrRadius, double mrAlsVel) { double flowRate = 2 * math.pi * math.pow(mrRadius, 2) * mrAlsVel; return flowRate; } @override double lvidIndex(double lvid, double bsa) { // [LVIDd|Distance"cm;2D"]/[BSA"m2;PatientInfo"] double value = lvid / bsa; return value; } @override double lvidN(double lvid, double weight) { // [LVIDs|Distance"cm;2D"]/pow([Weight"kg;PatientInfo"],0.294) double value = lvid / math.pow(weight, 0.294); return value; } /// /// Pht by DecT = 0.29 * dt /// /// s /// s @override double phtByDecT(double dt) { double pht = 0.29 * dt; return pht; } /// /// MVA by PHT = 22.0/MV PHT/100 cm^2 /// /// s /// (unit cm^2) @override double mvaByPht(double mvPht) { double area = 22.0 / mvPht / 100; return area; } } /// 实验室小鼠 class MouseCardiacFormulas extends BaseCardiacFormulas { MouseCardiacFormulas() : super(); } /// 人用普通公式 class AnimalsCardiacFormulas extends BaseCardiacFormulas { AnimalsCardiacFormulas() : super(); // @override // double edvTeichholz(double lvidd) { // double edv = double.nan; // double animalsLvidd = lvidd / 10; // if (!NumUtil.almostEquals(animalsLvidd, 0)) { // edv = 7.0 * math.pow(animalsLvidd, 3) / (2.4 + animalsLvidd); // } // return edv; // } /// CO @override double co( double sv, { required int hr, }) { return (sv - hr); } /// CI @override double ci( double sv, { required int hr, required double bsa, }) { return ((sv - hr)) / bsa; } /// ESV (Teichholz) /// /// Formula: `[7.0/(2.4 + LVIDs)] x LVIDs^3` /// /// [lvids] Unit: `cm` /// /// Result Unit: `cm³` @override double esvTeichholz(double lvids) { // 计算公式相同,入参不同 return edvTeichholz(lvids); } /// /// MVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/MV VTI /// AVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/AV VTI /// TVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/TV VTI /// PVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/PV VTI /// /// cm /// cm /// cm /// cm^2 /// SV(LVOT)|CO @override double flowAreaByVTI(double otDiam, double otvti, double vti) { double animalsOtDiam = otDiam / 10; double animalsOtvti = otvti / 10; double animalsVti = vti / 10; double sv = 0.25 * math.pi * math.pow(animalsOtDiam, 2) * animalsOtvti / animalsVti; return sv; } @override double lvdMass(double ivsd, double lvidd, double lvpwd) { final density = GlobalCardiacConfigs.density; const correctionFactor = GlobalCardiacConfigs.correctionFactor; double part1 = math.pow(ivsd + lvidd + lvpwd, 3).toDouble(); double part2 = math.pow(lvidd, 3).toDouble(); double value = ((density * part1 - part2) + correctionFactor) / 1000.0; return value; } }