cardiac.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. import 'dart:math' as math;
  2. import 'package:fis_measure/configs/cardiac.dart';
  3. import 'package:fis_measure/configs/patient.dart';
  4. import 'package:fis_measure/interfaces/enums/species.dart';
  5. import 'package:fis_measure/process/calcuators/formulas/general.dart';
  6. import 'package:fis_measure/utils/number.dart';
  7. class CardiacFormulas {
  8. static ICardiacFormulaStrategy _singleton = AnimalsCardiacFormulas();
  9. static void reinitialize() {
  10. if (GlobalPatientConfig.speciesType == SpeciesType.mouse) {
  11. GlobalCardiacConfigs.density = 1.05;
  12. _singleton = MouseCardiacFormulas();
  13. } else {
  14. GlobalCardiacConfigs.density = 1.04;
  15. _singleton = AnimalsCardiacFormulas();
  16. }
  17. }
  18. static double teiIndex(double co, double et) => _singleton.teiIndex(co, et);
  19. static double ef(double edv, double esv) => _singleton.ef(edv, esv);
  20. static double edvTeichholz(double lvidd) => _singleton.edvTeichholz(lvidd);
  21. static double edvCube(double lvidd) => _singleton.edvCube(lvidd);
  22. static double esvTeichholz(double lvids) => _singleton.esvTeichholz(lvids);
  23. static double esvCube(double lvids) => _singleton.esvCube(lvids);
  24. static double sv(double edv, double esv) => _singleton.sv(edv, esv);
  25. static double co(double sv, {required int hr}) => _singleton.co(sv, hr: hr);
  26. static double ci(double sv, {required int hr, required double bsa}) =>
  27. _singleton.ci(sv, hr: hr, bsa: bsa);
  28. static double lvdMass(double ivsd, double lvidd, double lvpwd) =>
  29. _singleton.lvdMass(ivsd, lvidd, lvpwd);
  30. static double lvdMassAL(
  31. double lvadSaxEpi, double lvadSaxEndo, double lvldApical) =>
  32. _singleton.lvdMassAL(lvadSaxEpi, lvadSaxEndo, lvldApical);
  33. static double lvdMassIndex(double lvdmass, double bsa) =>
  34. _singleton.lvdMassIndex(lvdmass, bsa);
  35. static double fsPercent(double lvidd, double lvids) =>
  36. _singleton.fsPercent(lvidd, lvids);
  37. static double ivsPercent(double ivss, double ivsd) =>
  38. _singleton.ivsPercent(ivss, ivsd);
  39. static double lvpwPercent(double lvpws, double lvpwd) =>
  40. _singleton.lvpwPercent(lvpws, lvpwd);
  41. static double mamPercent(double mapse, double lvidd, double lvids) =>
  42. _singleton.mamPercent(mapse, lvidd, lvids);
  43. static double si(double sv, double bsa) => _singleton.si(sv, bsa);
  44. static double flowAreaByVTI(double otDiam, double otvti, double vti) =>
  45. _singleton.flowAreaByVTI(otDiam, otvti, vti);
  46. static double dviByVTI(double otvti, double vti) =>
  47. _singleton.dviByVTI(otvti, vti);
  48. static double avaIndex(double avaByVTI, double bsa) =>
  49. _singleton.avaIndex(avaByVTI, bsa);
  50. static double lvevi(double lvev, double bsa) => _singleton.lvevi(lvev, bsa);
  51. static double lvSimsonVolume(double l, List<double>? a, List<double>? b,
  52. [int num = 20]) =>
  53. _singleton.lvSimsonVolume(l, a, b, num);
  54. static double mrEROA(double mrRadius, double mrAlsVel, double mrVmax) =>
  55. _singleton.mrEROA(mrRadius, mrAlsVel, mrVmax);
  56. static double mrRV(double mrEROA, double mrVti) =>
  57. _singleton.mrRV(mrEROA, mrVti);
  58. static double mrFlowRate(double mrRadius, double mrAlsVel) =>
  59. _singleton.mrFlowRate(mrRadius, mrAlsVel);
  60. static double lvidIndex(double lvid, double bsa) =>
  61. _singleton.lvidIndex(lvid, bsa);
  62. static double lvidN(double lvid, double weight) =>
  63. _singleton.lvidN(lvid, weight);
  64. // CardiacFormulas._internal(); // 私有构造函数
  65. static double bsa(double weight) {
  66. return 0.1 * math.pow(weight, 0.667);
  67. }
  68. }
  69. abstract class ICardiacFormulaStrategy {
  70. double teiIndex(double co, double et);
  71. double ef(double edv, double esv);
  72. double edvTeichholz(double lvidd);
  73. double edvCube(double lvidd);
  74. double esvTeichholz(double lvids);
  75. double esvCube(double lvids);
  76. double sv(double edv, double esv);
  77. double co(double sv, {required int hr});
  78. double ci(double sv, {required int hr, required double bsa});
  79. double lvdMass(double ivsd, double lvidd, double lvpwd);
  80. double lvdMassAL(double lvadSaxEpi, double lvadSaxEndo, double lvldApical);
  81. double lvdMassIndex(double lvdmass, double bsa);
  82. double fsPercent(double lvidd, double lvids);
  83. double ivsPercent(double ivss, double ivsd);
  84. double lvpwPercent(double lvpws, double lvpwd);
  85. double mamPercent(double mapse, double lvidd, double lvids);
  86. double si(double sv, double bsa);
  87. double flowAreaByVTI(double otDiam, double otvti, double vti);
  88. double dviByVTI(double otvti, double vti);
  89. double avaIndex(double avaByVTI, double bsa);
  90. double lvevi(double lvev, double bsa);
  91. double lvSimsonVolume(double l, List<double>? a, List<double>? b,
  92. [int num = 20]);
  93. double mrEROA(double mrRadius, double mrAlsVel, double mrVmax);
  94. double mrRV(double mrEROA, double mrVti);
  95. double mrFlowRate(double mrRadius, double mrAlsVel);
  96. /// LVID Index
  97. ///
  98. /// [lvid] cm
  99. ///
  100. /// [bsa] m2
  101. double lvidIndex(double lvid, double bsa);
  102. /// LVID N
  103. ///
  104. /// [lvid] cm
  105. ///
  106. /// [weight] kg
  107. double lvidN(double lvid, double weight);
  108. }
  109. /// 基础公式
  110. class BaseCardiacFormulas implements ICardiacFormulaStrategy {
  111. /// IMP
  112. ///
  113. /// Formula: `(CO-ET)/ET`
  114. ///
  115. /// Result Unit: `None`
  116. @override
  117. double teiIndex(double co, double et) {
  118. double imp = 0.0;
  119. if (et != 0.0) {
  120. imp = (((co).abs() - (et).abs()) / et).abs();
  121. }
  122. return imp;
  123. }
  124. /// EF
  125. /// Formula: `(EDV - ESV )/EDV`
  126. ///
  127. /// [edv] Unit: `cm³`
  128. ///
  129. /// [esv] Unit: `cm³`
  130. ///
  131. /// Result Unit: `None`
  132. @override
  133. double ef(double edv, double esv) {
  134. // 这行判断暂时注释掉是为了使实际表现与旧版一致
  135. // if (edv < esv) {
  136. // return double.nan;
  137. // }
  138. return (edv - esv) / edv * 100;
  139. }
  140. /// EDV (Teichholz)
  141. ///
  142. /// Formula: `[7.0/(2.4 + LVIDd)] x LVIDd^3`
  143. ///
  144. /// [lvidd] Unit: `cm`
  145. ///
  146. /// Result Unit: `cm³`
  147. @override
  148. double edvTeichholz(double lvidd) {
  149. double edv = double.nan;
  150. if (!NumUtil.almostEquals(lvidd, 0)) {
  151. edv = 7.0 * math.pow(lvidd, 3) / (2.4 + lvidd);
  152. }
  153. return edv;
  154. }
  155. /// EDV (Cube)
  156. ///
  157. /// Formula: `LVIDd^3`
  158. ///
  159. /// [lvidd] Unit: `cm`
  160. ///
  161. /// Result Unit: `cm³`
  162. @override
  163. double edvCube(double lvidd) {
  164. double edv = double.nan;
  165. if (!NumUtil.almostEquals(lvidd, 0)) {
  166. edv = math.pow(lvidd, 3).toDouble();
  167. }
  168. return edv;
  169. }
  170. /// ESV (Teichholz)
  171. ///
  172. /// Formula: `[7.0/(2.4 + LVIDs)] x LVIDs^3`
  173. ///
  174. /// [lvids] Unit: `cm`
  175. ///
  176. /// Result Unit: `cm³`
  177. @override
  178. double esvTeichholz(double lvids) {
  179. // 计算公式相同,入参不同
  180. return edvTeichholz(lvids);
  181. }
  182. /// ESV (Cube)
  183. ///
  184. /// Formula: `LVIDs^3`
  185. ///
  186. /// [lvids] Unit: `cm`
  187. ///
  188. /// Result Unit: `cm³`
  189. @override
  190. double esvCube(double lvids) {
  191. // 计算公式相同,入参不同
  192. return edvCube(lvids);
  193. }
  194. /// SV
  195. @override
  196. double sv(double edv, double esv) {
  197. return edv - esv;
  198. }
  199. /// CO
  200. @override
  201. double co(
  202. double sv, {
  203. required int hr,
  204. }) {
  205. return (sv - hr) / 1000.0;
  206. }
  207. /// CI
  208. @override
  209. double ci(
  210. double sv, {
  211. required int hr,
  212. required double bsa,
  213. }) {
  214. return ((sv - hr) / 1000.0) / bsa;
  215. }
  216. /// <summary>
  217. /// LVEVI = LVEV / BSA
  218. /// </summary>
  219. /// <param name="lvev">Unit cm³</param>
  220. /// <param name="bsa">Unit m²</param>
  221. /// <returns>cm³/m²</returns>
  222. @override
  223. double lvevi(double lvev, double bsa) {
  224. return lvev / bsa;
  225. }
  226. /// LVdMass
  227. /// LVd Mass(2D)
  228. @override
  229. double lvdMass(double ivsd, double lvidd, double lvpwd) {
  230. final density = GlobalCardiacConfigs.density;
  231. double part1 = math.pow(ivsd + lvidd + lvpwd, 3).toDouble();
  232. double part2 = math.pow(lvidd, 3).toDouble();
  233. double value = (density * (part1 - part2)) / 1000.0;
  234. return value;
  235. }
  236. /// LVd Mass AL
  237. @override
  238. double lvdMassAL(
  239. double lvadSaxEpi,
  240. double lvadSaxEndo,
  241. double lvldApical,
  242. ) {
  243. double t =
  244. math.sqrt(lvadSaxEpi / math.pi) - math.sqrt(lvadSaxEndo / math.pi);
  245. double mass = 1.05 *
  246. 5 /
  247. 6 *
  248. (lvadSaxEpi * (lvldApical + t) - lvadSaxEndo * lvldApical) /
  249. 1000;
  250. return mass;
  251. }
  252. /// LVd Mass Index
  253. @override
  254. double lvdMassIndex(double lvdmass, double bsa) {
  255. return lvdmass / bsa * 1000;
  256. }
  257. /// %FS
  258. @override
  259. double fsPercent(double lvidd, double lvids) {
  260. return ((lvidd - lvids) / lvidd) * 100;
  261. }
  262. /// %IVS
  263. @override
  264. double ivsPercent(double ivss, double ivsd) {
  265. return ((ivss - ivsd) / ivsd) * 100;
  266. }
  267. /// %LVPW
  268. @override
  269. double lvpwPercent(double lvpws, double lvpwd) {
  270. return ((lvpws - lvpwd) / lvpwd) * 100;
  271. }
  272. /// MAM%
  273. @override
  274. double mamPercent(double mapse, double lvidd, double lvids) {
  275. return mapse / (lvidd - lvids + mapse) * 100;
  276. }
  277. /// SI
  278. ///
  279. /// (EDV - ESV)/BSA
  280. ///
  281. /// [sv] cm³
  282. ///
  283. /// [bsa] m²
  284. ///
  285. /// return `cm³/m²`
  286. @override
  287. double si(double sv, double bsa) {
  288. double si = sv / bsa;
  289. return si;
  290. }
  291. /// <summary>
  292. /// <para>MVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/MV VTI </para>
  293. /// <para>AVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/AV VTI</para>
  294. /// <para>TVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/TV VTI</para>
  295. /// <para>PVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/PV VTI</para>
  296. /// </summary>
  297. /// <param name="otDiam">cm</param>
  298. /// <param name="otvti">cm</param>
  299. /// <param name="vti">cm</param>
  300. /// <returns>cm^2</returns>
  301. @override
  302. double flowAreaByVTI(double otDiam, double otvti, double vti) {
  303. double sv = 0.25 * math.pi * math.pow(otDiam, 2) * otvti / vti;
  304. return sv;
  305. }
  306. /// <param name="otvti">cm</param>
  307. /// <param name="vti">cm</param>
  308. /// <returns>Unit None</returns>
  309. @override
  310. double dviByVTI(double otvti, double vti) {
  311. double dvi = double.nan;
  312. if (!GeneralFormulas.doubleAlmostEquals(vti, 0)) {
  313. dvi = otvti / vti;
  314. }
  315. return dvi;
  316. }
  317. /// <summary>
  318. /// AVA Index = avaByVTI/bsa
  319. /// </summary>
  320. /// <param name="avaByVTI">cm2</param>
  321. /// <param name="bsa">m2</param>
  322. /// <returns>cm2/m2</returns>
  323. @override
  324. double avaIndex(double avaByVTI, double bsa) {
  325. double index = double.nan;
  326. if (!GeneralFormulas.doubleAlmostEquals(bsa, 0)) {
  327. index = avaByVTI / bsa;
  328. }
  329. return index;
  330. }
  331. /// <summary>
  332. /// Formular : V= π/4×∑_(i=1)^20▒〖(a_i×b_i)〗×L/20
  333. /// </summary>
  334. /// <returns></returns>
  335. @override
  336. double lvSimsonVolume(double l, List<double>? a, List<double>? b,
  337. [int num = 20]) {
  338. double volume = double.nan;
  339. if (a != null && b != null) {
  340. double sum = 0;
  341. for (int i = 0; i < num; i++) {
  342. sum += a[i] * b[i] * l / num;
  343. }
  344. volume = math.pi / 4 * sum;
  345. }
  346. return volume;
  347. }
  348. @override
  349. double mrEROA(double mrRadius, double mrAlsVel, double mrVmax) {
  350. double mrEROA = double.nan;
  351. if (!GeneralFormulas.doubleAlmostEquals(mrVmax, 0)) {
  352. mrEROA = 2 * math.pi * math.pow(mrRadius, 2) * mrAlsVel / (mrVmax).abs();
  353. }
  354. return mrEROA;
  355. }
  356. @override
  357. double mrRV(double mrEROA, double mrVti) {
  358. return mrEROA * mrVti;
  359. }
  360. @override
  361. double mrFlowRate(double mrRadius, double mrAlsVel) {
  362. double flowRate = 2 * math.pi * math.pow(mrRadius, 2) * mrAlsVel;
  363. return flowRate;
  364. }
  365. @override
  366. double lvidIndex(double lvid, double bsa) {
  367. // [LVIDd|Distance"cm;2D"]/[BSA"m2;PatientInfo"]
  368. double value = lvid / bsa;
  369. return value;
  370. }
  371. @override
  372. double lvidN(double lvid, double weight) {
  373. // [LVIDs|Distance"cm;2D"]/pow([Weight"kg;PatientInfo"],0.294)
  374. double value = lvid / math.pow(weight, 0.294);
  375. return value;
  376. }
  377. }
  378. /// 实验室小鼠
  379. class MouseCardiacFormulas extends BaseCardiacFormulas {
  380. MouseCardiacFormulas() : super();
  381. }
  382. /// 人用普通公式
  383. class AnimalsCardiacFormulas extends BaseCardiacFormulas {
  384. AnimalsCardiacFormulas() : super();
  385. // @override
  386. // double edvTeichholz(double lvidd) {
  387. // double edv = double.nan;
  388. // double animalsLvidd = lvidd / 10;
  389. // if (!NumUtil.almostEquals(animalsLvidd, 0)) {
  390. // edv = 7.0 * math.pow(animalsLvidd, 3) / (2.4 + animalsLvidd);
  391. // }
  392. // return edv;
  393. // }
  394. /// CO
  395. @override
  396. double co(
  397. double sv, {
  398. required int hr,
  399. }) {
  400. return (sv - hr);
  401. }
  402. /// CI
  403. @override
  404. double ci(
  405. double sv, {
  406. required int hr,
  407. required double bsa,
  408. }) {
  409. return ((sv - hr)) / bsa;
  410. }
  411. /// ESV (Teichholz)
  412. ///
  413. /// Formula: `[7.0/(2.4 + LVIDs)] x LVIDs^3`
  414. ///
  415. /// [lvids] Unit: `cm`
  416. ///
  417. /// Result Unit: `cm³`
  418. @override
  419. double esvTeichholz(double lvids) {
  420. // 计算公式相同,入参不同
  421. return edvTeichholz(lvids);
  422. }
  423. /// <summary>
  424. /// <para>MVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/MV VTI </para>
  425. /// <para>AVA VTI = 1/4 x π x (LVOT Diam)^2 x LVOT VTI/AV VTI</para>
  426. /// <para>TVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/TV VTI</para>
  427. /// <para>PVA VTI = 1/4 x π x (RVOT Diam)^2 x RVOT VTI/PV VTI</para>
  428. /// </summary>
  429. /// <param name="otDiam">cm</param>
  430. /// <param name="otvti">cm</param>
  431. /// <param name="vti">cm</param>
  432. /// <returns>cm^2</returns>
  433. /// SV(LVOT)|CO
  434. @override
  435. double flowAreaByVTI(double otDiam, double otvti, double vti) {
  436. double animalsOtDiam = otDiam / 10;
  437. double animalsOtvti = otvti / 10;
  438. double animalsVti = vti / 10;
  439. double sv =
  440. 0.25 * math.pi * math.pow(animalsOtDiam, 2) * animalsOtvti / animalsVti;
  441. return sv;
  442. }
  443. @override
  444. double lvdMass(double ivsd, double lvidd, double lvpwd) {
  445. final density = GlobalCardiacConfigs.density;
  446. const correctionFactor = GlobalCardiacConfigs.correctionFactor;
  447. double part1 = math.pow(ivsd + lvidd + lvpwd, 3).toDouble();
  448. double part2 = math.pow(lvidd, 3).toDouble();
  449. double value = ((density * part1 - part2) + correctionFactor) / 1000.0;
  450. return value;
  451. }
  452. }