trace.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. import 'package:fis_measure/interfaces/date_types/point.dart';
  2. import 'package:fis_measure/interfaces/date_types/vector.dart';
  3. import 'package:fis_measure/interfaces/process/items/terms.dart';
  4. import 'package:fis_measure/process/calcuators/formulas/cardiac.dart';
  5. import 'package:fis_measure/process/calcuators/formulas/general.dart';
  6. import 'package:fis_measure/process/items/item_feature.dart';
  7. import 'package:fis_measure/process/primitives/multi_method/dop_trace_disp/cardiac_cycle.dart';
  8. import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
  9. import 'dart:math' as math;
  10. import 'calculator.dart';
  11. class TraceCal extends Calculator<TraceItemAbstract, double> {
  12. TraceCal(TraceItemAbstract ref) : super(ref);
  13. @override
  14. void calculate() {
  15. if (ref.feature == null) return;
  16. final feature = ref.feature!;
  17. final viewport = feature.hostVisualArea!.viewport!;
  18. // 加入画布偏移量
  19. final canvasOffset = feature.hostVisualArea!.layoutRegion!.topLeft;
  20. //加入坐标系偏移量
  21. final coordinateOffset = viewport.region;
  22. final regionPoints = feature.innerPoints
  23. .map((e) => viewport
  24. .convert(
  25. e.clone().addVector(DVector(-canvasOffset.x, -canvasOffset.y)))
  26. .addVector(DVector(coordinateOffset.left, -coordinateOffset.top)))
  27. .toList();
  28. final yFlippedPoints = regionPoints.map((e) => DPoint(e.x, -e.y)).toList();
  29. double fakeOutputDate = 0;
  30. var countVTIResult = GeneralFormulas.countVTI(yFlippedPoints);
  31. Map<String, double> result = calculateDopplerTrace(yFlippedPoints);
  32. var outputVTI = countVTIResult[0];
  33. var outputVTIMean = countVTIResult[0];
  34. var outputTiEnv = countVTIResult[1];
  35. var outputTAMAX = countVTIResult[2];
  36. var outputTAMEAN = countVTIResult[2];
  37. var outputVelocityMax = countVTIResult[3];
  38. var outputVelocityMean = countVTIResult[3];
  39. var outputMPG = countVTIResult[4];
  40. var outputMMPG = countVTIResult[4];
  41. var outputHR = countVTIResult[5];
  42. for (var output in ref.meta.outputs) {
  43. ///TODO:[Gavin] 实现以下计算逻辑
  44. switch (output.name) {
  45. case MeasureTerms.TAMAX:
  46. feature.updateFloatValue(output, outputTAMAX, output.unit);
  47. break;
  48. case MeasureTerms.TAMEAN:
  49. feature.updateFloatValue(output, outputTAMEAN, output.unit);
  50. break;
  51. case MeasureTerms.PS:
  52. if (result[MeasureTerms.PS] != null) {
  53. feature.updateFloatValue(
  54. output, result[MeasureTerms.PS]!, output.unit);
  55. }
  56. break;
  57. case MeasureTerms.ED:
  58. if (result[MeasureTerms.ED] != null) {
  59. feature.updateFloatValue(
  60. output, result[MeasureTerms.ED]!, output.unit);
  61. }
  62. break;
  63. case MeasureTerms.MD:
  64. if (result[MeasureTerms.MD] != null) {
  65. feature.updateFloatValue(
  66. output, result[MeasureTerms.MD]!, output.unit);
  67. }
  68. break;
  69. case MeasureTerms.HeartRate:
  70. feature.updateFloatValue(output, outputHR, output.unit);
  71. break;
  72. case MeasureTerms.Acceleration:
  73. if (result[MeasureTerms.Acceleration] != null) {
  74. feature.updateFloatValue(
  75. output, result[MeasureTerms.Acceleration]!, output.unit);
  76. }
  77. break;
  78. case MeasureTerms.AT:
  79. if (result[MeasureTerms.AT] != null) {
  80. feature.updateFloatValue(
  81. output, result[MeasureTerms.AT]!, output.unit);
  82. }
  83. break;
  84. case MeasureTerms.PSED:
  85. if (result[MeasureTerms.PSED] != null) {
  86. feature.updateFloatValue(
  87. output, result[MeasureTerms.PSED]!, output.unit);
  88. }
  89. break;
  90. case MeasureTerms.EDPS:
  91. if (result[MeasureTerms.EDPS] != null) {
  92. feature.updateFloatValue(
  93. output, result[MeasureTerms.EDPS]!, output.unit);
  94. }
  95. break;
  96. case MeasureTerms.PI:
  97. if (result[MeasureTerms.PI] != null) {
  98. feature.updateFloatValue(
  99. output, result[MeasureTerms.PI]!, output.unit);
  100. }
  101. break;
  102. case MeasureTerms.PIMD:
  103. if (result[MeasureTerms.PIMD] != null) {
  104. feature.updateFloatValue(
  105. output, result[MeasureTerms.PIMD]!, output.unit);
  106. }
  107. break;
  108. case MeasureTerms.RI:
  109. if (result[MeasureTerms.RI] != null) {
  110. feature.updateFloatValue(
  111. output, result[MeasureTerms.RI]!, output.unit);
  112. }
  113. break;
  114. case MeasureTerms.RIMD:
  115. if (result[MeasureTerms.RIMD] != null) {
  116. feature.updateFloatValue(
  117. output, result[MeasureTerms.RIMD]!, output.unit);
  118. }
  119. break;
  120. case MeasureTerms.MaxPG:
  121. feature.updateFloatValue(
  122. output, result[MeasureTerms.MaxPG]!, output.unit);
  123. break;
  124. case MeasureTerms.VelocityMax:
  125. feature.updateFloatValue(output, outputVelocityMax, output.unit);
  126. break;
  127. case MeasureTerms.VelocityMean:
  128. feature.updateFloatValue(output, outputVelocityMean, output.unit);
  129. break;
  130. case MeasureTerms.PeakPG:
  131. feature.updateFloatValue(
  132. output, result[MeasureTerms.PeakPG]!, output.unit);
  133. break;
  134. case MeasureTerms.VTI:
  135. feature.updateFloatValue(output, outputVTI, output.unit);
  136. break;
  137. case MeasureTerms.VTIMean:
  138. feature.updateFloatValue(output, outputVTIMean, output.unit);
  139. break;
  140. case MeasureTerms.MPG:
  141. feature.updateFloatValue(output, outputMPG, output.unit);
  142. break;
  143. case MeasureTerms.MMPG:
  144. feature.updateFloatValue(output, outputMMPG, output.unit);
  145. break;
  146. case MeasureTerms.TiEnv:
  147. // var outputTiEnv = GeneralFormulas.countEnvelopeTime(points);
  148. feature.updateFloatValue(output, outputTiEnv, output.unit);
  149. break;
  150. case MeasureTerms.EVEL:
  151. if (result[MeasureTerms.EVEL] != null) {
  152. feature.updateFloatValue(
  153. output, result[MeasureTerms.EVEL]!, output.unit);
  154. }
  155. break;
  156. case MeasureTerms.AVEL:
  157. if (result[MeasureTerms.AVEL] != null) {
  158. feature.updateFloatValue(
  159. output, result[MeasureTerms.AVEL]!, output.unit);
  160. }
  161. break;
  162. case MeasureTerms.EARatio:
  163. if (result[MeasureTerms.EARatio] != null) {
  164. feature.updateFloatValue(
  165. output, result[MeasureTerms.EARatio]!, output.unit);
  166. }
  167. break;
  168. case MeasureTerms.DT:
  169. if (result[MeasureTerms.DT] != null) {
  170. feature.updateFloatValue(
  171. output, result[MeasureTerms.DT]!, output.unit);
  172. }
  173. break;
  174. case MeasureTerms.PHT:
  175. if (result[MeasureTerms.PHT] != null) {
  176. feature.updateFloatValue(
  177. output, result[MeasureTerms.PHT]!, output.unit);
  178. }
  179. break;
  180. case MeasureTerms.VA:
  181. if (result[MeasureTerms.VA] != null) {
  182. feature.updateFloatValue(
  183. output, result[MeasureTerms.VA]!, output.unit);
  184. }
  185. break;
  186. case MeasureTerms.ADur:
  187. if (result[MeasureTerms.ADur] != null) {
  188. feature.updateFloatValue(
  189. output, result[MeasureTerms.ADur]!, output.unit);
  190. }
  191. break;
  192. case MeasureTerms.ATDTRatio:
  193. if (result[MeasureTerms.ATDTRatio] != null) {
  194. feature.updateFloatValue(
  195. output, result[MeasureTerms.ATDTRatio]!, output.unit);
  196. }
  197. break;
  198. case MeasureTerms.ATETRatio:
  199. if (result[MeasureTerms.ATETRatio] != null) {
  200. feature.updateFloatValue(
  201. output, result[MeasureTerms.ATETRatio]!, output.unit);
  202. }
  203. break;
  204. // case MeasureTerms.Trace:
  205. // feature.updateFloatValue(output, fakeOutputDate, output.unit);
  206. // break;
  207. default:
  208. break;
  209. }
  210. }
  211. }
  212. static List<DPoint> getFeatureYFlippedPoints(MeasureItemFeature feature) {
  213. final viewport = feature.hostVisualArea!.viewport!;
  214. // 加入画布偏移量
  215. final canvasOffset = feature.hostVisualArea!.layoutRegion!.topLeft;
  216. //加入坐标系偏移量
  217. final coordinateOffset = viewport.region;
  218. final regionPoints = feature.innerPoints
  219. .map((e) => viewport
  220. .convert(
  221. e.clone().addVector(DVector(-canvasOffset.x, -canvasOffset.y)))
  222. .addVector(DVector(coordinateOffset.left, -coordinateOffset.top)))
  223. .toList();
  224. final points = regionPoints.map((e) => DPoint(e.x, -e.y)).toList();
  225. return points;
  226. }
  227. static List<double> getCountVTI(MeasureItemFeature feature) {
  228. final yFlippedPoints = getFeatureYFlippedPoints(feature);
  229. final result = GeneralFormulas.countVTI(yFlippedPoints);
  230. return result;
  231. }
  232. /// 获取到值之后
  233. Map<String, double> calculateHeartCycleRelevantValues(
  234. CardiacCycle heartCycle,
  235. List<DPoint> maxTraceLineOfCycle,
  236. List<DPoint> meanTraceLineOfCycle,
  237. ) {
  238. double pv = double.nan;
  239. double vtiMean = double.nan;
  240. double taMean = double.nan;
  241. double mmpg = double.nan;
  242. if (meanTraceLineOfCycle.isNotEmpty) {
  243. vtiMean = GeneralFormulas.countVTI(meanTraceLineOfCycle).first;
  244. }
  245. double vti = double.nan;
  246. double taMax = double.nan;
  247. double mpg = double.nan;
  248. double ps = double.nan;
  249. double ed = double.nan;
  250. double md = double.nan;
  251. double acctime = double.nan;
  252. double accel = double.nan;
  253. if (maxTraceLineOfCycle.isNotEmpty) {
  254. List<double> vtiResult = GeneralFormulas.countVTI(maxTraceLineOfCycle);
  255. vti = vtiResult.first;
  256. ps = heartCycle.peakSystolic.y;
  257. ed = heartCycle.diastoleEnd.y;
  258. md = heartCycle.minimumAbsoluteVelocity.y;
  259. taMax = vtiResult[2];
  260. pv = vtiResult[3];
  261. mpg = vtiResult[4];
  262. mmpg = vtiResult[4];
  263. acctime = heartCycle.peakSystolic.x - heartCycle.systoleStart.x;
  264. accel = (heartCycle.peakSystolic.y - heartCycle.systoleStart.y).abs() /
  265. acctime;
  266. }
  267. double velE = double.nan;
  268. double velA = double.nan;
  269. double dt = double.nan;
  270. double pht = double.nan;
  271. double va = double.nan;
  272. double aDur = double.nan;
  273. double eAration = double.nan;
  274. if (heartCycle.ePeak != DPoint.zero && heartCycle.ePeak != null) {
  275. velE = heartCycle.ePeak!.y;
  276. if (heartCycle.ePeakEnd != DPoint.zero &&
  277. heartCycle.ePeak != DPoint.zero) {
  278. acctime = heartCycle.ePeak!.x - heartCycle.systoleStart.x;
  279. accel =
  280. (heartCycle.ePeak!.y - heartCycle.systoleStart.y).abs() / acctime;
  281. }
  282. }
  283. if (heartCycle.aPeak != DPoint.zero && heartCycle.aPeak != null) {
  284. velA = heartCycle.aPeak!.y;
  285. }
  286. if (heartCycle.ePeak != DPoint.zero && heartCycle.aPeak != DPoint.zero) {
  287. eAration = (velE - velA).abs();
  288. }
  289. if (heartCycle.ePeak != DPoint.zero &&
  290. heartCycle.ePeakEnd != DPoint.zero &&
  291. heartCycle.aPeak != null &&
  292. heartCycle.ePeakEnd != null) {
  293. dt = (heartCycle.ePeakEnd!.x - heartCycle.ePeak!.x).abs();
  294. pht = CardiacFormulas.phtByDecT(dt);
  295. va = CardiacFormulas.mvaByPht(pht);
  296. }
  297. if (heartCycle.aPeakStart != DPoint.zero && heartCycle.aPeakStart != null) {
  298. aDur = (heartCycle.diastoleEnd.x - heartCycle.aPeakStart!.x).abs();
  299. }
  300. return {
  301. MeasureTerms.PS: ps,
  302. MeasureTerms.ED: ed,
  303. MeasureTerms.MD: md,
  304. MeasureTerms.VelocityMax: pv,
  305. MeasureTerms.AT: acctime,
  306. MeasureTerms.Acceleration: accel,
  307. MeasureTerms.TAMAX: taMax,
  308. MeasureTerms.TAMEAN: taMean,
  309. MeasureTerms.VTI: vti,
  310. MeasureTerms.VTIMean: vtiMean,
  311. MeasureTerms.MPG: mpg,
  312. MeasureTerms.MMPG: mmpg,
  313. MeasureTerms.EVEL: velE,
  314. MeasureTerms.AVEL: velA,
  315. MeasureTerms.EARatio: eAration,
  316. MeasureTerms.DT: dt,
  317. MeasureTerms.PHT: pht,
  318. MeasureTerms.VA: va,
  319. MeasureTerms.ADur: aDur,
  320. };
  321. }
  322. double ratio(double d1, double d2) {
  323. return d1 / d2;
  324. }
  325. Map<String, double> calculateDopplerTrace([
  326. List<DPoint>? maxTraceLineOfCycle,
  327. ]) {
  328. double cyclesStart = double.maxFinite;
  329. double cyclesEnd = double.minPositive;
  330. DPoint systoleStart = maxTraceLineOfCycle!.first;
  331. DPoint diastoleEnd = maxTraceLineOfCycle.last;
  332. double maxDistance = 0;
  333. DPoint peakSystolic = DPoint(0, 0);
  334. for (var i = 1; i < maxTraceLineOfCycle.length; i++) {
  335. final point = maxTraceLineOfCycle[i];
  336. final distance = (point.y - systoleStart.y).abs();
  337. if (distance > maxDistance) {
  338. maxDistance = distance;
  339. peakSystolic = point;
  340. }
  341. }
  342. List<CardiacCycle> validSystoleCycles = [
  343. CardiacCycle(
  344. diastoleEnd: diastoleEnd,
  345. systoleStart: systoleStart,
  346. minimumAbsoluteVelocity: DPoint(0, 0),
  347. index: 0,
  348. peakSystolic: peakSystolic,
  349. )
  350. ];
  351. Map<String, double> baseValues = {};
  352. /// 获取全量的测量值的方法 dopplerTrace.AvgHeartCycle > 0 && dopplerTrace.CardiacCycles.Count >= dopplerTrace.AvgHeartCycle
  353. for (CardiacCycle i in validSystoleCycles) {
  354. cyclesStart = math.min(i.systoleStart.x, cyclesStart);
  355. cyclesEnd = math.max(i.diastoleEnd.x, cyclesEnd);
  356. /// TODO 这边需要看超声机代码,继续捞值
  357. // List<DPoint> maxTraceLineOfCycle = [];
  358. List<DPoint> meanTraceLineOfCycle = [];
  359. baseValues = calculateHeartCycleRelevantValues(
  360. i,
  361. maxTraceLineOfCycle,
  362. meanTraceLineOfCycle,
  363. );
  364. }
  365. double? ps = baseValues[MeasureTerms.PS];
  366. double? ed = baseValues[MeasureTerms.ED];
  367. double? md = baseValues[MeasureTerms.MD];
  368. double? taMax = baseValues[MeasureTerms.TAMAX];
  369. if (ps != null) {
  370. if (ed != null) {
  371. double maxPG = GeneralFormulas.maxPG(ps, ed);
  372. double psed = ratio(ps, ed).abs();
  373. double edps = ratio(ed, ps).abs();
  374. double ri = GeneralFormulas.countRI(ps, ed);
  375. double velocityMean = GeneralFormulas.medianVelocity(ps, ed);
  376. baseValues.addAll({
  377. MeasureTerms.MaxPG: maxPG,
  378. MeasureTerms.PSED: psed,
  379. MeasureTerms.EDPS: edps,
  380. MeasureTerms.RI: ri.abs(),
  381. MeasureTerms.VelocityMean: velocityMean,
  382. });
  383. if (taMax != null) {
  384. double pi = GeneralFormulas.pi(ps, ed, taMax);
  385. baseValues.addAll({
  386. MeasureTerms.PI: pi,
  387. });
  388. }
  389. double vMean = GeneralFormulas.medianVelocity(ps, ed);
  390. double piTCD = GeneralFormulas.pi(ps, ed, vMean);
  391. if (!piTCD.isNaN) {
  392. baseValues.addAll({
  393. MeasureTerms.PITCD: piTCD,
  394. });
  395. }
  396. }
  397. if (md != null) {
  398. double rimd = GeneralFormulas.countRI(ps, md).abs();
  399. baseValues.addAll({
  400. MeasureTerms.RIMD: rimd,
  401. });
  402. if (taMax != null) {
  403. double piMd = GeneralFormulas.pi(ps, md, taMax);
  404. if (!piMd.isNaN) {
  405. baseValues.addAll({
  406. MeasureTerms.PIMD: piMd,
  407. });
  408. }
  409. }
  410. }
  411. }
  412. double? pv = baseValues[MeasureTerms.VelocityMax];
  413. if (pv != null) {
  414. double peakPG = GeneralFormulas.countPressure(pv);
  415. baseValues.addAll({
  416. MeasureTerms.PeakPG: peakPG,
  417. });
  418. }
  419. double cycleEnvTimeSpan = (cyclesEnd - cyclesStart).abs();
  420. if (cycleEnvTimeSpan > 0) {
  421. /// dopplerTrace.AvgHeartCycle, 需要获取
  422. double? hr = GeneralFormulas.countHR(cycleEnvTimeSpan);
  423. baseValues.addAll({
  424. MeasureTerms.HeartRate: hr,
  425. MeasureTerms.TiEnv: cycleEnvTimeSpan,
  426. });
  427. }
  428. double? velE = baseValues[MeasureTerms.EVEL];
  429. double? velA = baseValues[MeasureTerms.AVEL];
  430. if (velA != null && velE != null) {
  431. double? eARatio = ratio(velA, velE).abs();
  432. baseValues.addAll({
  433. MeasureTerms.EARatio: eARatio,
  434. });
  435. }
  436. double? at = baseValues[MeasureTerms.AT];
  437. double? dt = baseValues[MeasureTerms.DT];
  438. double? et = baseValues[MeasureTerms.TiEnv];
  439. if (at != null && dt != null && et != null) {
  440. double? atdt = ratio(at, dt).abs();
  441. double? atet = ratio(at, et).abs();
  442. baseValues.addAll({
  443. MeasureTerms.ATDTRatio: atdt,
  444. MeasureTerms.ATETRatio: atet,
  445. });
  446. }
  447. return baseValues;
  448. }
  449. Map<String, double> tryCalculateByTrace(
  450. List<DPoint> traceLine,
  451. double envStart,
  452. double envEnd,
  453. bool isMax,
  454. ) {
  455. double vti = double.nan;
  456. double tiEnv = double.nan;
  457. double ta = double.nan;
  458. double pv = double.nan;
  459. double meanPG = double.nan;
  460. if (traceLine.isNotEmpty) {
  461. List<DPoint> line = [];
  462. for (var point in traceLine) {
  463. if (point.x.almostNotLessThan(point.x, envStart) &&
  464. point.x.almostNotGreaterThan(point.x, envEnd)) {
  465. line.add(point);
  466. }
  467. }
  468. if (line.isNotEmpty) {
  469. List<double> vueResult = GeneralFormulas.countVTI(line);
  470. vti = vueResult.first;
  471. var isShowAbsValue = true; // 假设这是从某个服务获取的值
  472. if (isShowAbsValue) {
  473. ta = vueResult[2].abs();
  474. pv = vueResult[3].abs();
  475. tiEnv = vueResult[1];
  476. meanPG = vueResult[4];
  477. }
  478. }
  479. }
  480. if (isMax) {
  481. return {
  482. MeasureTerms.TAMAX: ta,
  483. MeasureTerms.VTI: vti,
  484. MeasureTerms.MPG: meanPG,
  485. MeasureTerms.TiEnv: tiEnv,
  486. MeasureTerms.VelocityMax: pv
  487. };
  488. }
  489. return {
  490. MeasureTerms.TAMEAN: ta,
  491. MeasureTerms.VTIMean: vti,
  492. MeasureTerms.MMPG: meanPG,
  493. MeasureTerms.TiEnv: tiEnv,
  494. MeasureTerms.VelocityMax: pv,
  495. MeasureTerms.PeakPG: pv,
  496. };
  497. }
  498. }
  499. extension DoubleExtensions on double {
  500. bool almostEquals(double other, double precision) {
  501. if (isNaN && other.isNaN) {
  502. return true;
  503. }
  504. return (this - other).abs() <= precision.abs();
  505. }
  506. bool almostNotLessThan(double double1, double double2,
  507. [double precision = 0.000001]) {
  508. if (double1 < double2) {
  509. return true;
  510. }
  511. return double1.almostEquals(double2, precision);
  512. }
  513. bool almostNotGreaterThan(double double1, double double2,
  514. [double precision = 0.000001]) {
  515. if (double1 > double2) {
  516. return true;
  517. }
  518. return double1.almostEquals(double2, precision);
  519. }
  520. }