cardiac.dart 12 KB

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