semiauto_trace.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. // ignore_for_file: invalid_use_of_protected_member
  2. import 'package:fis_measure/interfaces/date_types/point.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/dop_trace_disp/data.dart';
  9. import 'dart:math' as math;
  10. import 'package:fis_measure/process/primitives/multi_method/semiauto_trace.dart';
  11. import 'calculator.dart';
  12. class SemiautoTraceCal extends Calculator<TraceItemAbstract, double> {
  13. SemiautoTraceCal(TraceItemAbstract ref) : super(ref);
  14. double cyclesStart = double.maxFinite;
  15. double cyclesEnd = 0;
  16. int validSystoleCyclesLength = 0;
  17. @override
  18. void calculate() {
  19. if (ref.feature == null) return;
  20. Map<String, double> calculateDopplerTraceResult = {};
  21. SemiautoTraceFeature feature = ref.feature! as SemiautoTraceFeature;
  22. /// 最大点集
  23. List<DPoint> maxPhysicalPonints = TraceListData.aboveMaxPonints;
  24. List<CardiacCycle> validSystoleCycles = feature.currentCardiacCycleList;
  25. if (validSystoleCycles.isEmpty) {
  26. List<DPoint> regionPoints = feature.innerPoints
  27. .map((e) => convertTimeMotionPoint(feature, e))
  28. .toList();
  29. final yFlippedPoints = regionPoints;
  30. double min = math.min(yFlippedPoints.first.x, yFlippedPoints.last.x);
  31. double max = math.max(yFlippedPoints.first.x, yFlippedPoints.last.x);
  32. calculateDopplerTraceResult = calculateDopplerTrace(
  33. yFlippedPoints,
  34. min,
  35. max,
  36. );
  37. }
  38. if (validSystoleCyclesLength != validSystoleCycles.length) {
  39. print(validSystoleCyclesLength);
  40. for (int i = 0; i < validSystoleCycles.length; i++) {
  41. /// 获取物理点坐标
  42. List<DPoint> regionPoints = getPoints(
  43. maxPhysicalPonints,
  44. validSystoleCycles[i],
  45. );
  46. final points = regionPoints.map((e) {
  47. final currentPoint = convertTimeMotionPoint(feature, e);
  48. return DPoint(currentPoint.x, currentPoint.y);
  49. }).toList();
  50. if (points.isEmpty) {
  51. return;
  52. }
  53. double min = math.min(points.first.x, points.last.x);
  54. double max = math.max(points.first.x, points.last.x);
  55. calculateDopplerTraceResult = calculateDopplerTrace(
  56. points,
  57. min,
  58. max,
  59. validSystoleCycles[i],
  60. );
  61. }
  62. if (validSystoleCycles.isEmpty) {
  63. feature.initValues();
  64. }
  65. validSystoleCyclesLength = validSystoleCycles.length;
  66. }
  67. for (var output in ref.meta.outputs) {
  68. ///TODO:[Gavin] 实现以下计算逻辑
  69. switch (output.name) {
  70. case MeasureTerms.Placeholder:
  71. feature.updateStringValue(output, "", output.unit);
  72. break;
  73. case MeasureTerms.TAMAX:
  74. if (calculateDopplerTraceResult[MeasureTerms.TAMAX] != null) {
  75. feature.updateFloatValue(output,
  76. calculateDopplerTraceResult[MeasureTerms.TAMAX]!, output.unit);
  77. }
  78. break;
  79. case MeasureTerms.TAMEAN:
  80. if (calculateDopplerTraceResult[MeasureTerms.TAMEAN] != null) {
  81. feature.updateFloatValue(output,
  82. calculateDopplerTraceResult[MeasureTerms.TAMEAN]!, output.unit);
  83. }
  84. break;
  85. case MeasureTerms.PS:
  86. if (calculateDopplerTraceResult[MeasureTerms.PS] != null) {
  87. feature.updateFloatValue(output,
  88. calculateDopplerTraceResult[MeasureTerms.PS]!, output.unit);
  89. }
  90. break;
  91. case MeasureTerms.ED:
  92. if (calculateDopplerTraceResult[MeasureTerms.ED] != null) {
  93. feature.updateFloatValue(output,
  94. calculateDopplerTraceResult[MeasureTerms.ED]!, output.unit);
  95. }
  96. break;
  97. case MeasureTerms.MD:
  98. if (calculateDopplerTraceResult[MeasureTerms.MD] != null) {
  99. feature.updateFloatValue(output,
  100. calculateDopplerTraceResult[MeasureTerms.MD]!, output.unit);
  101. }
  102. break;
  103. case MeasureTerms.HeartRate:
  104. if (calculateDopplerTraceResult[MeasureTerms.HeartRate] != null) {
  105. feature.updateFloatValue(
  106. output,
  107. calculateDopplerTraceResult[MeasureTerms.HeartRate]!,
  108. output.unit);
  109. }
  110. break;
  111. case MeasureTerms.Acceleration:
  112. if (calculateDopplerTraceResult[MeasureTerms.Acceleration] != null) {
  113. feature.updateFloatValue(
  114. output,
  115. calculateDopplerTraceResult[MeasureTerms.Acceleration]!,
  116. output.unit);
  117. }
  118. break;
  119. case MeasureTerms.AT:
  120. if (calculateDopplerTraceResult[MeasureTerms.AT] != null) {
  121. feature.updateFloatValue(output,
  122. calculateDopplerTraceResult[MeasureTerms.AT]!, output.unit);
  123. }
  124. break;
  125. case MeasureTerms.PSED:
  126. if (calculateDopplerTraceResult[MeasureTerms.PSED] != null) {
  127. feature.updateFloatValue(output,
  128. calculateDopplerTraceResult[MeasureTerms.PSED]!, output.unit);
  129. }
  130. break;
  131. case MeasureTerms.EDPS:
  132. if (calculateDopplerTraceResult[MeasureTerms.EDPS] != null) {
  133. feature.updateFloatValue(output,
  134. calculateDopplerTraceResult[MeasureTerms.EDPS]!, output.unit);
  135. }
  136. break;
  137. case MeasureTerms.PI:
  138. if (calculateDopplerTraceResult[MeasureTerms.PI] != null) {
  139. feature.updateFloatValue(output,
  140. calculateDopplerTraceResult[MeasureTerms.PI]!, output.unit);
  141. }
  142. break;
  143. case MeasureTerms.PIMD:
  144. if (calculateDopplerTraceResult[MeasureTerms.PIMD] != null) {
  145. feature.updateFloatValue(output,
  146. calculateDopplerTraceResult[MeasureTerms.PIMD]!, output.unit);
  147. }
  148. break;
  149. case MeasureTerms.RI:
  150. if (calculateDopplerTraceResult[MeasureTerms.RI] != null) {
  151. feature.updateFloatValue(output,
  152. calculateDopplerTraceResult[MeasureTerms.RI]!, output.unit);
  153. }
  154. break;
  155. case MeasureTerms.RIMD:
  156. if (calculateDopplerTraceResult[MeasureTerms.RIMD] != null) {
  157. feature.updateFloatValue(output,
  158. calculateDopplerTraceResult[MeasureTerms.RIMD]!, output.unit);
  159. }
  160. break;
  161. case MeasureTerms.MaxPG:
  162. if (calculateDopplerTraceResult[MeasureTerms.MaxPG] != null) {
  163. feature.updateFloatValue(output,
  164. calculateDopplerTraceResult[MeasureTerms.MaxPG]!, output.unit);
  165. }
  166. break;
  167. case MeasureTerms.VelocityMax:
  168. if (calculateDopplerTraceResult[MeasureTerms.VelocityMax] != null) {
  169. feature.updateFloatValue(
  170. output,
  171. calculateDopplerTraceResult[MeasureTerms.VelocityMax]!,
  172. output.unit);
  173. }
  174. break;
  175. case MeasureTerms.VelocityMean:
  176. if (calculateDopplerTraceResult[MeasureTerms.VelocityMean] != null) {
  177. feature.updateFloatValue(
  178. output,
  179. calculateDopplerTraceResult[MeasureTerms.VelocityMean]!,
  180. output.unit);
  181. }
  182. break;
  183. case MeasureTerms.PeakPG:
  184. if (calculateDopplerTraceResult[MeasureTerms.PeakPG] != null) {
  185. feature.updateFloatValue(output,
  186. calculateDopplerTraceResult[MeasureTerms.PeakPG]!, output.unit);
  187. }
  188. break;
  189. case MeasureTerms.VTI:
  190. if (calculateDopplerTraceResult[MeasureTerms.VTI] != null) {
  191. feature.updateFloatValue(output,
  192. calculateDopplerTraceResult[MeasureTerms.VTI]!, output.unit);
  193. }
  194. break;
  195. case MeasureTerms.VTIMean:
  196. if (calculateDopplerTraceResult[MeasureTerms.VTIMean] != null) {
  197. feature.updateFloatValue(
  198. output,
  199. calculateDopplerTraceResult[MeasureTerms.VTIMean]!,
  200. output.unit);
  201. }
  202. break;
  203. case MeasureTerms.MPG:
  204. if (calculateDopplerTraceResult[MeasureTerms.MPG] != null) {
  205. feature.updateFloatValue(output,
  206. calculateDopplerTraceResult[MeasureTerms.MPG]!, output.unit);
  207. }
  208. break;
  209. case MeasureTerms.MMPG:
  210. if (calculateDopplerTraceResult[MeasureTerms.MMPG] != null) {
  211. feature.updateFloatValue(output,
  212. calculateDopplerTraceResult[MeasureTerms.MMPG]!, output.unit);
  213. }
  214. break;
  215. case MeasureTerms.TiEnv:
  216. // var outputTiEnv = GeneralFormulas.countEnvelopeTime(points);
  217. if (calculateDopplerTraceResult[MeasureTerms.TiEnv] != null) {
  218. feature.updateFloatValue(output,
  219. calculateDopplerTraceResult[MeasureTerms.TiEnv]!, output.unit);
  220. }
  221. break;
  222. case MeasureTerms.EVEL:
  223. if (calculateDopplerTraceResult[MeasureTerms.EVEL] != null) {
  224. feature.updateFloatValue(output,
  225. calculateDopplerTraceResult[MeasureTerms.EVEL]!, output.unit);
  226. }
  227. break;
  228. case MeasureTerms.AVEL:
  229. if (calculateDopplerTraceResult[MeasureTerms.AVEL] != null) {
  230. feature.updateFloatValue(output,
  231. calculateDopplerTraceResult[MeasureTerms.AVEL]!, output.unit);
  232. }
  233. break;
  234. case MeasureTerms.EARatio:
  235. if (calculateDopplerTraceResult[MeasureTerms.EARatio] != null) {
  236. feature.updateFloatValue(
  237. output,
  238. calculateDopplerTraceResult[MeasureTerms.EARatio]!,
  239. output.unit);
  240. }
  241. break;
  242. case MeasureTerms.DT:
  243. if (calculateDopplerTraceResult[MeasureTerms.DT] != null) {
  244. feature.updateFloatValue(output,
  245. calculateDopplerTraceResult[MeasureTerms.DT]!, output.unit);
  246. }
  247. break;
  248. case MeasureTerms.PHT:
  249. if (calculateDopplerTraceResult[MeasureTerms.PHT] != null) {
  250. feature.updateFloatValue(output,
  251. calculateDopplerTraceResult[MeasureTerms.PHT]!, output.unit);
  252. }
  253. break;
  254. case MeasureTerms.VA:
  255. if (calculateDopplerTraceResult[MeasureTerms.VA] != null) {
  256. feature.updateFloatValue(output,
  257. calculateDopplerTraceResult[MeasureTerms.VA]!, output.unit);
  258. }
  259. break;
  260. case MeasureTerms.ADur:
  261. if (calculateDopplerTraceResult[MeasureTerms.ADur] != null) {
  262. feature.updateFloatValue(output,
  263. calculateDopplerTraceResult[MeasureTerms.ADur]!, output.unit);
  264. }
  265. break;
  266. case MeasureTerms.ATDTRatio:
  267. if (calculateDopplerTraceResult[MeasureTerms.ATDTRatio] != null) {
  268. feature.updateFloatValue(
  269. output,
  270. calculateDopplerTraceResult[MeasureTerms.ATDTRatio]!,
  271. output.unit);
  272. }
  273. break;
  274. case MeasureTerms.ATETRatio:
  275. if (calculateDopplerTraceResult[MeasureTerms.ATETRatio] != null) {
  276. feature.updateFloatValue(
  277. output,
  278. calculateDopplerTraceResult[MeasureTerms.ATETRatio]!,
  279. output.unit);
  280. }
  281. break;
  282. // case MeasureTerms.Trace:
  283. // if (calculateDopplerTraceResult[MeasureTerms.TAMAX] != null) {
  284. // break;
  285. default:
  286. break;
  287. }
  288. }
  289. }
  290. List<DPoint> getPoints(
  291. List<DPoint> lines,
  292. CardiacCycle cycle,
  293. ) {
  294. // Size displaySize = Get.find<IApplication>().displaySize;
  295. List<DPoint> points = [];
  296. for (DPoint line in lines) {
  297. // DPoint logicPoint = convert2LogicPoint(line);
  298. final systoleStartX = cycle.systoleStart.x;
  299. final diastoleEndX = cycle.diastoleEnd.x;
  300. if (line.x >= systoleStartX && line.x <= diastoleEndX) {
  301. points.add(DPoint(line.x, line.y));
  302. }
  303. }
  304. return points;
  305. }
  306. List<DPoint> getFeatureYFlippedPoints(MeasureItemFeature feature) {
  307. final regionPoints = feature.innerPoints
  308. .map((e) => convertTimeMotionPoint(feature, e))
  309. .toList();
  310. return regionPoints;
  311. }
  312. List<double> getCountVTI(MeasureItemFeature feature) {
  313. final yFlippedPoints = getFeatureYFlippedPoints(feature);
  314. final result = GeneralFormulas.countVTI(yFlippedPoints);
  315. return result;
  316. }
  317. /// 获取到值之后
  318. Map<String, double> calculateHeartCycleRelevantValues(
  319. CardiacCycle heartCycle,
  320. List<DPoint> maxTraceLineOfCycle,
  321. List<DPoint> meanTraceLineOfCycle,
  322. ) {
  323. double pv = double.nan;
  324. double vtiMean = double.nan;
  325. double taMean = double.nan;
  326. double mmpg = double.nan;
  327. if (meanTraceLineOfCycle.isNotEmpty) {
  328. List<double> meanResult = GeneralFormulas.countVTI(meanTraceLineOfCycle);
  329. vtiMean = meanResult.first;
  330. taMean = meanResult[2];
  331. }
  332. double vti = double.nan;
  333. double taMax = double.nan;
  334. double mpg = double.nan;
  335. double ps = double.nan;
  336. double ed = double.nan;
  337. double md = double.nan;
  338. double acctime = double.nan;
  339. double accel = double.nan;
  340. if (maxTraceLineOfCycle.isNotEmpty) {
  341. List<double> vtiResult = GeneralFormulas.countVTI(maxTraceLineOfCycle);
  342. vti = vtiResult.first;
  343. ps = heartCycle.peakSystolic.y;
  344. ed = heartCycle.diastoleEnd.y;
  345. md = heartCycle.minimumAbsoluteVelocity.y;
  346. taMax = vtiResult[2];
  347. pv = vtiResult[3];
  348. mpg = vtiResult[4];
  349. mmpg = vtiResult[4];
  350. acctime = heartCycle.peakSystolic.x - heartCycle.systoleStart.x;
  351. accel = (heartCycle.peakSystolic.y - heartCycle.systoleStart.y).abs() /
  352. acctime;
  353. }
  354. double velE = double.nan;
  355. double velA = double.nan;
  356. double dt = double.nan;
  357. double pht = double.nan;
  358. double va = double.nan;
  359. double aDur = double.nan;
  360. double eAration = double.nan;
  361. if (heartCycle.ePeak != DPoint.zero && heartCycle.ePeak != null) {
  362. velE = heartCycle.ePeak!.y;
  363. if (heartCycle.ePeakEnd != DPoint.zero &&
  364. heartCycle.ePeak != DPoint.zero) {
  365. acctime = heartCycle.ePeak!.x - heartCycle.systoleStart.x;
  366. accel =
  367. (heartCycle.ePeak!.y - heartCycle.systoleStart.y).abs() / acctime;
  368. }
  369. }
  370. if (heartCycle.aPeak != DPoint.zero && heartCycle.aPeak != null) {
  371. velA = heartCycle.aPeak!.y;
  372. }
  373. if (heartCycle.ePeak != DPoint.zero && heartCycle.aPeak != DPoint.zero) {
  374. eAration = (velE - velA).abs();
  375. }
  376. if (heartCycle.ePeak != DPoint.zero &&
  377. heartCycle.ePeakEnd != DPoint.zero &&
  378. heartCycle.aPeak != null &&
  379. heartCycle.ePeakEnd != null) {
  380. dt = (heartCycle.ePeakEnd!.x - heartCycle.ePeak!.x).abs();
  381. pht = CardiacFormulas.phtByDecT(dt);
  382. va = CardiacFormulas.mvaByPht(pht);
  383. }
  384. if (heartCycle.aPeakStart != DPoint.zero && heartCycle.aPeakStart != null) {
  385. aDur = (heartCycle.diastoleEnd.x - heartCycle.aPeakStart!.x).abs();
  386. }
  387. return {
  388. MeasureTerms.PS: ps,
  389. MeasureTerms.ED: ed,
  390. MeasureTerms.MD: md,
  391. MeasureTerms.VelocityMax: pv,
  392. MeasureTerms.AT: acctime,
  393. MeasureTerms.Acceleration: accel,
  394. MeasureTerms.TAMAX: taMax,
  395. MeasureTerms.TAMEAN: taMean,
  396. MeasureTerms.VTI: vti,
  397. MeasureTerms.VTIMean: vtiMean,
  398. MeasureTerms.MPG: mpg,
  399. MeasureTerms.MMPG: mmpg,
  400. MeasureTerms.EVEL: velE,
  401. MeasureTerms.AVEL: velA,
  402. MeasureTerms.EARatio: eAration,
  403. MeasureTerms.DT: dt,
  404. MeasureTerms.PHT: pht,
  405. MeasureTerms.VA: va,
  406. MeasureTerms.ADur: aDur,
  407. };
  408. }
  409. double ratio(double d1, double d2) {
  410. return d1 / d2;
  411. }
  412. Map<String, double> calculateDopplerTrace([
  413. List<DPoint>? maxTraceLine,
  414. double envStart = double.nan,
  415. double envEnd = double.nan,
  416. CardiacCycle? cardiacCycle,
  417. ]) {
  418. Map<String, double> baseValues = {};
  419. if (cardiacCycle != null) {
  420. CardiacCycle transitionCardiacCycle = CardiacCycle(
  421. diastoleEnd: DPoint(0, 0),
  422. systoleStart: DPoint(0, 0),
  423. peakSystolic: DPoint(0, 0),
  424. minimumAbsoluteVelocity: DPoint(0, 0),
  425. index: 0,
  426. );
  427. transitionCardiacCycle.peakSystolic =
  428. convertTimeMotionPoint(ref.feature!, cardiacCycle.peakSystolic);
  429. transitionCardiacCycle.systoleStart =
  430. convertTimeMotionPoint(ref.feature!, cardiacCycle.systoleStart);
  431. transitionCardiacCycle.diastoleEnd =
  432. convertTimeMotionPoint(ref.feature!, cardiacCycle.diastoleEnd);
  433. transitionCardiacCycle.index = cardiacCycle.index;
  434. cyclesStart = math.min(
  435. transitionCardiacCycle.systoleStart.x,
  436. cyclesStart,
  437. );
  438. cyclesEnd = math.min(
  439. transitionCardiacCycle.diastoleEnd.x,
  440. cyclesEnd,
  441. );
  442. List<DPoint> maxTraceLineOfCycle = maxTraceLine ?? [];
  443. List<DPoint> meanTraceLineOfCycle =
  444. maxTraceLine?.map((e) => DPoint(e.x, e.y * 0.75)).toList() ?? [];
  445. baseValues = calculateHeartCycleRelevantValues(
  446. transitionCardiacCycle,
  447. maxTraceLineOfCycle,
  448. meanTraceLineOfCycle,
  449. );
  450. double? ps = baseValues[MeasureTerms.PS];
  451. double? ed = baseValues[MeasureTerms.ED];
  452. double? md = baseValues[MeasureTerms.MD];
  453. double? taMax = baseValues[MeasureTerms.TAMAX];
  454. if (ps != null) {
  455. if (ed != null) {
  456. double maxPG = GeneralFormulas.maxPG(ps, ed);
  457. double psed = ratio(ps, ed).abs();
  458. double edps = ratio(ed, ps).abs();
  459. double ri = GeneralFormulas.countRI(ps, ed);
  460. double velocityMean = GeneralFormulas.medianVelocity(ps, ed);
  461. baseValues.addAll({
  462. MeasureTerms.MaxPG: maxPG,
  463. MeasureTerms.PSED: psed,
  464. MeasureTerms.EDPS: edps,
  465. MeasureTerms.RI: ri.abs(),
  466. MeasureTerms.VelocityMean: velocityMean,
  467. });
  468. if (taMax != null) {
  469. double pi = GeneralFormulas.pi(ps, ed, taMax);
  470. baseValues.addAll({
  471. MeasureTerms.PI: pi,
  472. });
  473. }
  474. double vMean = GeneralFormulas.medianVelocity(ps, ed);
  475. double piTCD = GeneralFormulas.pi(ps, ed, vMean);
  476. if (!piTCD.isNaN) {
  477. baseValues.addAll({
  478. MeasureTerms.PITCD: piTCD,
  479. });
  480. }
  481. }
  482. if (md != null) {
  483. double rimd = GeneralFormulas.countRI(ps, md).abs();
  484. baseValues.addAll({
  485. MeasureTerms.RIMD: rimd,
  486. });
  487. if (taMax != null) {
  488. double piMd = GeneralFormulas.pi(ps, md, taMax);
  489. if (!piMd.isNaN) {
  490. baseValues.addAll({
  491. MeasureTerms.PIMD: piMd,
  492. });
  493. }
  494. }
  495. }
  496. }
  497. double? pv = baseValues[MeasureTerms.VelocityMax];
  498. if (pv != null) {
  499. double peakPG = GeneralFormulas.countPressure(pv);
  500. baseValues.addAll({
  501. MeasureTerms.PeakPG: peakPG,
  502. });
  503. }
  504. double cycleEnvTimeSpan = (cyclesEnd - cyclesStart).abs();
  505. if (cycleEnvTimeSpan > 0) {
  506. /// dopplerTrace.AvgHeartCycle, 需要获取
  507. double? hr = GeneralFormulas.countHR(cycleEnvTimeSpan);
  508. baseValues.addAll({
  509. MeasureTerms.HeartRate: hr,
  510. MeasureTerms.TiEnv: cycleEnvTimeSpan,
  511. });
  512. }
  513. double? velE = baseValues[MeasureTerms.EVEL];
  514. double? velA = baseValues[MeasureTerms.AVEL];
  515. if (velA != null && velE != null) {
  516. double? eARatio = ratio(velA, velE).abs();
  517. baseValues.addAll({
  518. MeasureTerms.EARatio: eARatio,
  519. });
  520. }
  521. } else if (!envStart.isNaN &&
  522. !envEnd.isNaN &&
  523. !envStart.almostEquals(double.minPositive, 0.000001) &&
  524. !envEnd.almostEquals(double.minPositive, 0.000001)) {
  525. // List<DPoint> maxTraceLine = [];
  526. // List<DPoint> meanTraceLine = [];
  527. Map<String, double> meanValues = tryCalculateByTrace(
  528. maxTraceLine!.map((e) => DPoint(e.x, e.y * 0.75)).toList(),
  529. envStart,
  530. envEnd,
  531. false,
  532. );
  533. Map<String, double> maxValues = tryCalculateByTrace(
  534. maxTraceLine,
  535. envStart,
  536. envEnd,
  537. true,
  538. );
  539. baseValues.addAll(meanValues);
  540. baseValues.addAll(maxValues);
  541. }
  542. double? at = baseValues[MeasureTerms.AT];
  543. double? dt = baseValues[MeasureTerms.DT];
  544. double? et = baseValues[MeasureTerms.TiEnv];
  545. if (at != null && dt != null && et != null) {
  546. double? atdt = ratio(at, dt).abs();
  547. double? atet = ratio(at, et).abs();
  548. baseValues.addAll({
  549. MeasureTerms.ATDTRatio: atdt,
  550. MeasureTerms.ATETRatio: atet,
  551. });
  552. }
  553. return baseValues;
  554. }
  555. Map<String, double> tryCalculateByTrace(
  556. List<DPoint> traceLine,
  557. double envStart,
  558. double envEnd,
  559. bool isMax,
  560. ) {
  561. double vti = double.nan;
  562. double tiEnv = double.nan;
  563. double ta = double.nan;
  564. double pv = double.nan;
  565. double meanPG = double.nan;
  566. if (traceLine.isNotEmpty) {
  567. List<DPoint> line = [];
  568. for (var point in traceLine) {
  569. line.add(point);
  570. // if (point.x.almostNotLessThan(point.x, envStart) &&
  571. // point.x.almostNotGreaterThan(point.x, envEnd)) {
  572. // line.add(point);
  573. // }
  574. }
  575. if (line.isNotEmpty) {
  576. List<double> vueResult = GeneralFormulas.countVTI(line);
  577. vti = vueResult.first;
  578. var isShowAbsValue = true; // 假设这是从某个服务获取的值
  579. if (isShowAbsValue) {
  580. ta = vueResult[2].abs();
  581. pv = vueResult[3].abs();
  582. tiEnv = vueResult[1];
  583. meanPG = vueResult[4];
  584. }
  585. }
  586. }
  587. if (isMax) {
  588. return {
  589. MeasureTerms.TAMAX: ta,
  590. MeasureTerms.VTI: vti,
  591. MeasureTerms.MPG: meanPG,
  592. MeasureTerms.TiEnv: tiEnv,
  593. MeasureTerms.VelocityMax: pv
  594. };
  595. }
  596. return {
  597. MeasureTerms.TAMEAN: ta,
  598. MeasureTerms.VTIMean: vti,
  599. MeasureTerms.MMPG: meanPG,
  600. MeasureTerms.TiEnv: tiEnv,
  601. MeasureTerms.VelocityMax: pv,
  602. MeasureTerms.PeakPG: pv,
  603. };
  604. }
  605. }
  606. extension DoubleExtensions on double {
  607. bool almostEquals(double other, double precision) {
  608. if (isNaN && other.isNaN) {
  609. return true;
  610. }
  611. return (this - other).abs() <= precision.abs();
  612. }
  613. bool almostNotLessThan(double double1, double double2,
  614. [double precision = 0.000001]) {
  615. if (double1 < double2) {
  616. return true;
  617. }
  618. return double1.almostEquals(double2, precision);
  619. }
  620. bool almostNotGreaterThan(double double1, double double2,
  621. [double precision = 0.000001]) {
  622. if (double1 > double2) {
  623. return true;
  624. }
  625. return double1.almostEquals(double2, precision);
  626. }
  627. }