|
@@ -0,0 +1,666 @@
|
|
|
+// ignore_for_file: invalid_use_of_protected_member
|
|
|
+
|
|
|
+import 'package:fis_measure/interfaces/date_types/point.dart';
|
|
|
+import 'package:fis_measure/interfaces/process/items/terms.dart';
|
|
|
+import 'package:fis_measure/process/calcuators/formulas/cardiac.dart';
|
|
|
+import 'package:fis_measure/process/calcuators/formulas/general.dart';
|
|
|
+import 'package:fis_measure/process/items/item_feature.dart';
|
|
|
+import 'package:fis_measure/process/primitives/multi_method/auto_doppler_trace.dart';
|
|
|
+import 'package:fis_measure/process/primitives/multi_method/dop_trace_disp/cardiac_cycle.dart';
|
|
|
+import 'package:fis_measure/process/primitives/multi_method/dop_trace_disp/data.dart';
|
|
|
+import 'dart:math' as math;
|
|
|
+import 'calculator.dart';
|
|
|
+
|
|
|
+class AutoDopplerTraceCal extends Calculator<TraceItemAbstract, double> {
|
|
|
+ AutoDopplerTraceCal(TraceItemAbstract ref) : super(ref);
|
|
|
+ double cyclesStart = double.maxFinite;
|
|
|
+ double cyclesEnd = 0;
|
|
|
+ int validSystoleCyclesLength = 0;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void calculate() {
|
|
|
+ if (ref.feature == null) return;
|
|
|
+
|
|
|
+ Map<String, double> calculateDopplerTraceResult = {};
|
|
|
+
|
|
|
+ AutoDopplerTraceFeature feature = ref.feature! as AutoDopplerTraceFeature;
|
|
|
+
|
|
|
+ /// 最大点集
|
|
|
+ List<DPoint> maxPhysicalPonints = TraceListData.aboveMaxPonints;
|
|
|
+
|
|
|
+ List<CardiacCycle> validSystoleCycles = feature.currentCardiacCycleList;
|
|
|
+
|
|
|
+ if (validSystoleCycles.isEmpty) {
|
|
|
+ List<DPoint> regionPoints = feature.innerPoints
|
|
|
+ .map((e) => convertTimeMotionPoint(feature, e))
|
|
|
+ .toList();
|
|
|
+ final yFlippedPoints = regionPoints;
|
|
|
+ double min = math.min(yFlippedPoints.first.x, yFlippedPoints.last.x);
|
|
|
+ double max = math.max(yFlippedPoints.first.x, yFlippedPoints.last.x);
|
|
|
+ calculateDopplerTraceResult = calculateDopplerTrace(
|
|
|
+ yFlippedPoints,
|
|
|
+ min,
|
|
|
+ max,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (validSystoleCyclesLength != validSystoleCycles.length) {
|
|
|
+ print(validSystoleCyclesLength);
|
|
|
+ for (int i = 0; i < validSystoleCycles.length; i++) {
|
|
|
+ /// 获取物理点坐标
|
|
|
+ List<DPoint> regionPoints = getPoints(
|
|
|
+ maxPhysicalPonints,
|
|
|
+ validSystoleCycles[i],
|
|
|
+ );
|
|
|
+ final points = regionPoints.map((e) {
|
|
|
+ final currentPoint = convertTimeMotionPoint(feature, e);
|
|
|
+ return DPoint(currentPoint.x, currentPoint.y);
|
|
|
+ }).toList();
|
|
|
+ if (points.isEmpty) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ double min = math.min(points.first.x, points.last.x);
|
|
|
+ double max = math.max(points.first.x, points.last.x);
|
|
|
+
|
|
|
+ calculateDopplerTraceResult = calculateDopplerTrace(
|
|
|
+ points,
|
|
|
+ min,
|
|
|
+ max,
|
|
|
+ validSystoleCycles[i],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (validSystoleCycles.isEmpty) {
|
|
|
+ feature.initValues();
|
|
|
+ }
|
|
|
+ validSystoleCyclesLength = validSystoleCycles.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var output in ref.meta.outputs) {
|
|
|
+ ///TODO:[Gavin] 实现以下计算逻辑
|
|
|
+ switch (output.name) {
|
|
|
+ case MeasureTerms.Placeholder:
|
|
|
+ feature.updateStringValue(output, "", output.unit);
|
|
|
+ break;
|
|
|
+ case MeasureTerms.TAMAX:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.TAMAX] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.TAMAX]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.TAMEAN:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.TAMEAN] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.TAMEAN]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PS:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PS] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PS]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.ED:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.ED] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.ED]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.MD:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.MD] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.MD]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.HeartRate:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.HeartRate] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.HeartRate]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.Acceleration:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.Acceleration] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.Acceleration]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.AT:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.AT] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.AT]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PSED:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PSED] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PSED]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.EDPS:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.EDPS] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.EDPS]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PI:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PI] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PI]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PIMD:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PIMD] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PIMD]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.RI:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.RI] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.RI]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.RIMD:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.RIMD] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.RIMD]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.MaxPG:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.MaxPG] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.MaxPG]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.VelocityMax:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.VelocityMax] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.VelocityMax]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.VelocityMean:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.VelocityMean] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.VelocityMean]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PeakPG:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PeakPG] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PeakPG]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.VTI:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.VTI] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.VTI]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.VTIMean:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.VTIMean] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.VTIMean]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.MPG:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.MPG] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.MPG]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.MMPG:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.MMPG] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.MMPG]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.TiEnv:
|
|
|
+ // var outputTiEnv = GeneralFormulas.countEnvelopeTime(points);
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.TiEnv] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.TiEnv]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.EVEL:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.EVEL] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.EVEL]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.AVEL:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.AVEL] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.AVEL]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.EARatio:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.EARatio] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.EARatio]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.DT:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.DT] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.DT]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.PHT:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.PHT] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.PHT]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.VA:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.VA] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.VA]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.ADur:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.ADur] != null) {
|
|
|
+ feature.updateFloatValue(output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.ADur]!, output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.ATDTRatio:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.ATDTRatio] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.ATDTRatio]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MeasureTerms.ATETRatio:
|
|
|
+ if (calculateDopplerTraceResult[MeasureTerms.ATETRatio] != null) {
|
|
|
+ feature.updateFloatValue(
|
|
|
+ output,
|
|
|
+ calculateDopplerTraceResult[MeasureTerms.ATETRatio]!,
|
|
|
+ output.unit);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ // case MeasureTerms.Trace:
|
|
|
+ // if (calculateDopplerTraceResult[MeasureTerms.TAMAX] != null) {
|
|
|
+
|
|
|
+ // break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<DPoint> getPoints(
|
|
|
+ List<DPoint> lines,
|
|
|
+ CardiacCycle cycle,
|
|
|
+ ) {
|
|
|
+ // Size displaySize = Get.find<IApplication>().displaySize;
|
|
|
+ List<DPoint> points = [];
|
|
|
+ for (DPoint line in lines) {
|
|
|
+ // DPoint logicPoint = convert2LogicPoint(line);
|
|
|
+ final systoleStartX = cycle.systoleStart.x;
|
|
|
+ final diastoleEndX = cycle.diastoleEnd.x;
|
|
|
+ if (line.x >= systoleStartX && line.x <= diastoleEndX) {
|
|
|
+ points.add(DPoint(line.x, line.y));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return points;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<DPoint> getFeatureYFlippedPoints(MeasureItemFeature feature) {
|
|
|
+ final regionPoints = feature.innerPoints
|
|
|
+ .map((e) => convertTimeMotionPoint(feature, e))
|
|
|
+ .toList();
|
|
|
+ return regionPoints;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<double> getCountVTI(MeasureItemFeature feature) {
|
|
|
+ final yFlippedPoints = getFeatureYFlippedPoints(feature);
|
|
|
+ final result = GeneralFormulas.countVTI(yFlippedPoints);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 获取到值之后
|
|
|
+ Map<String, double> calculateHeartCycleRelevantValues(
|
|
|
+ CardiacCycle heartCycle,
|
|
|
+ List<DPoint> maxTraceLineOfCycle,
|
|
|
+ List<DPoint> meanTraceLineOfCycle,
|
|
|
+ ) {
|
|
|
+ double pv = double.nan;
|
|
|
+ double vtiMean = double.nan;
|
|
|
+ double taMean = double.nan;
|
|
|
+ double mmpg = double.nan;
|
|
|
+ if (meanTraceLineOfCycle.isNotEmpty) {
|
|
|
+ List<double> meanResult = GeneralFormulas.countVTI(meanTraceLineOfCycle);
|
|
|
+ vtiMean = meanResult.first;
|
|
|
+ taMean = meanResult[2];
|
|
|
+ }
|
|
|
+
|
|
|
+ double vti = double.nan;
|
|
|
+ double taMax = double.nan;
|
|
|
+ double mpg = double.nan;
|
|
|
+ double ps = double.nan;
|
|
|
+ double ed = double.nan;
|
|
|
+ double md = double.nan;
|
|
|
+ double acctime = double.nan;
|
|
|
+ double accel = double.nan;
|
|
|
+ if (maxTraceLineOfCycle.isNotEmpty) {
|
|
|
+ List<double> vtiResult = GeneralFormulas.countVTI(maxTraceLineOfCycle);
|
|
|
+
|
|
|
+ vti = vtiResult.first;
|
|
|
+ ps = heartCycle.peakSystolic.y;
|
|
|
+ ed = heartCycle.diastoleEnd.y;
|
|
|
+ md = heartCycle.minimumAbsoluteVelocity.y;
|
|
|
+ taMax = vtiResult[2];
|
|
|
+ pv = vtiResult[3];
|
|
|
+ mpg = vtiResult[4];
|
|
|
+ mmpg = vtiResult[4];
|
|
|
+ acctime = heartCycle.peakSystolic.x - heartCycle.systoleStart.x;
|
|
|
+ accel = (heartCycle.peakSystolic.y - heartCycle.systoleStart.y).abs() /
|
|
|
+ acctime;
|
|
|
+ }
|
|
|
+ double velE = double.nan;
|
|
|
+ double velA = double.nan;
|
|
|
+ double dt = double.nan;
|
|
|
+ double pht = double.nan;
|
|
|
+ double va = double.nan;
|
|
|
+ double aDur = double.nan;
|
|
|
+ double eAration = double.nan;
|
|
|
+
|
|
|
+ if (heartCycle.ePeak != DPoint.zero && heartCycle.ePeak != null) {
|
|
|
+ velE = heartCycle.ePeak!.y;
|
|
|
+ if (heartCycle.ePeakEnd != DPoint.zero &&
|
|
|
+ heartCycle.ePeak != DPoint.zero) {
|
|
|
+ acctime = heartCycle.ePeak!.x - heartCycle.systoleStart.x;
|
|
|
+ accel =
|
|
|
+ (heartCycle.ePeak!.y - heartCycle.systoleStart.y).abs() / acctime;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (heartCycle.aPeak != DPoint.zero && heartCycle.aPeak != null) {
|
|
|
+ velA = heartCycle.aPeak!.y;
|
|
|
+ }
|
|
|
+ if (heartCycle.ePeak != DPoint.zero && heartCycle.aPeak != DPoint.zero) {
|
|
|
+ eAration = (velE - velA).abs();
|
|
|
+ }
|
|
|
+ if (heartCycle.ePeak != DPoint.zero &&
|
|
|
+ heartCycle.ePeakEnd != DPoint.zero &&
|
|
|
+ heartCycle.aPeak != null &&
|
|
|
+ heartCycle.ePeakEnd != null) {
|
|
|
+ dt = (heartCycle.ePeakEnd!.x - heartCycle.ePeak!.x).abs();
|
|
|
+ pht = CardiacFormulas.phtByDecT(dt);
|
|
|
+ va = CardiacFormulas.mvaByPht(pht);
|
|
|
+ }
|
|
|
+ if (heartCycle.aPeakStart != DPoint.zero && heartCycle.aPeakStart != null) {
|
|
|
+ aDur = (heartCycle.diastoleEnd.x - heartCycle.aPeakStart!.x).abs();
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ MeasureTerms.PS: ps,
|
|
|
+ MeasureTerms.ED: ed,
|
|
|
+ MeasureTerms.MD: md,
|
|
|
+ MeasureTerms.VelocityMax: pv,
|
|
|
+ MeasureTerms.AT: acctime,
|
|
|
+ MeasureTerms.Acceleration: accel,
|
|
|
+ MeasureTerms.TAMAX: taMax,
|
|
|
+ MeasureTerms.TAMEAN: taMean,
|
|
|
+ MeasureTerms.VTI: vti,
|
|
|
+ MeasureTerms.VTIMean: vtiMean,
|
|
|
+ MeasureTerms.MPG: mpg,
|
|
|
+ MeasureTerms.MMPG: mmpg,
|
|
|
+ MeasureTerms.EVEL: velE,
|
|
|
+ MeasureTerms.AVEL: velA,
|
|
|
+ MeasureTerms.EARatio: eAration,
|
|
|
+ MeasureTerms.DT: dt,
|
|
|
+ MeasureTerms.PHT: pht,
|
|
|
+ MeasureTerms.VA: va,
|
|
|
+ MeasureTerms.ADur: aDur,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ double ratio(double d1, double d2) {
|
|
|
+ return d1 / d2;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, double> calculateDopplerTrace([
|
|
|
+ List<DPoint>? maxTraceLine,
|
|
|
+ double envStart = double.nan,
|
|
|
+ double envEnd = double.nan,
|
|
|
+ CardiacCycle? cardiacCycle,
|
|
|
+ ]) {
|
|
|
+ Map<String, double> baseValues = {};
|
|
|
+
|
|
|
+ if (cardiacCycle != null) {
|
|
|
+ CardiacCycle transitionCardiacCycle = CardiacCycle(
|
|
|
+ diastoleEnd: DPoint(0, 0),
|
|
|
+ systoleStart: DPoint(0, 0),
|
|
|
+ peakSystolic: DPoint(0, 0),
|
|
|
+ minimumAbsoluteVelocity: DPoint(0, 0),
|
|
|
+ index: 0,
|
|
|
+ );
|
|
|
+ transitionCardiacCycle.peakSystolic =
|
|
|
+ convertTimeMotionPoint(ref.feature!, cardiacCycle.peakSystolic);
|
|
|
+ transitionCardiacCycle.systoleStart =
|
|
|
+ convertTimeMotionPoint(ref.feature!, cardiacCycle.systoleStart);
|
|
|
+ transitionCardiacCycle.diastoleEnd =
|
|
|
+ convertTimeMotionPoint(ref.feature!, cardiacCycle.diastoleEnd);
|
|
|
+
|
|
|
+ transitionCardiacCycle.index = cardiacCycle.index;
|
|
|
+
|
|
|
+ cyclesStart = math.min(
|
|
|
+ transitionCardiacCycle.systoleStart.x,
|
|
|
+ cyclesStart,
|
|
|
+ );
|
|
|
+ cyclesEnd = math.min(
|
|
|
+ transitionCardiacCycle.diastoleEnd.x,
|
|
|
+ cyclesEnd,
|
|
|
+ );
|
|
|
+
|
|
|
+ List<DPoint> maxTraceLineOfCycle = maxTraceLine ?? [];
|
|
|
+ List<DPoint> meanTraceLineOfCycle =
|
|
|
+ maxTraceLine?.map((e) => DPoint(e.x, e.y * 0.75)).toList() ?? [];
|
|
|
+ baseValues = calculateHeartCycleRelevantValues(
|
|
|
+ transitionCardiacCycle,
|
|
|
+ maxTraceLineOfCycle,
|
|
|
+ meanTraceLineOfCycle,
|
|
|
+ );
|
|
|
+ double? ps = baseValues[MeasureTerms.PS];
|
|
|
+ double? ed = baseValues[MeasureTerms.ED];
|
|
|
+ double? md = baseValues[MeasureTerms.MD];
|
|
|
+ double? taMax = baseValues[MeasureTerms.TAMAX];
|
|
|
+
|
|
|
+ if (ps != null) {
|
|
|
+ if (ed != null) {
|
|
|
+ double maxPG = GeneralFormulas.maxPG(ps, ed);
|
|
|
+ double psed = ratio(ps, ed).abs();
|
|
|
+ double edps = ratio(ed, ps).abs();
|
|
|
+ double ri = GeneralFormulas.countRI(ps, ed);
|
|
|
+ double velocityMean = GeneralFormulas.medianVelocity(ps, ed);
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.MaxPG: maxPG,
|
|
|
+ MeasureTerms.PSED: psed,
|
|
|
+ MeasureTerms.EDPS: edps,
|
|
|
+ MeasureTerms.RI: ri.abs(),
|
|
|
+ MeasureTerms.VelocityMean: velocityMean,
|
|
|
+ });
|
|
|
+ if (taMax != null) {
|
|
|
+ double pi = GeneralFormulas.pi(ps, ed, taMax);
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.PI: pi,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ double vMean = GeneralFormulas.medianVelocity(ps, ed);
|
|
|
+ double piTCD = GeneralFormulas.pi(ps, ed, vMean);
|
|
|
+ if (!piTCD.isNaN) {
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.PITCD: piTCD,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (md != null) {
|
|
|
+ double rimd = GeneralFormulas.countRI(ps, md).abs();
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.RIMD: rimd,
|
|
|
+ });
|
|
|
+ if (taMax != null) {
|
|
|
+ double piMd = GeneralFormulas.pi(ps, md, taMax);
|
|
|
+ if (!piMd.isNaN) {
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.PIMD: piMd,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ double? pv = baseValues[MeasureTerms.VelocityMax];
|
|
|
+
|
|
|
+ if (pv != null) {
|
|
|
+ double peakPG = GeneralFormulas.countPressure(pv);
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.PeakPG: peakPG,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ double cycleEnvTimeSpan = (cyclesEnd - cyclesStart).abs();
|
|
|
+
|
|
|
+ if (cycleEnvTimeSpan > 0) {
|
|
|
+ /// dopplerTrace.AvgHeartCycle, 需要获取
|
|
|
+ double? hr = GeneralFormulas.countHR(cycleEnvTimeSpan);
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.HeartRate: hr,
|
|
|
+ MeasureTerms.TiEnv: cycleEnvTimeSpan,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ double? velE = baseValues[MeasureTerms.EVEL];
|
|
|
+ double? velA = baseValues[MeasureTerms.AVEL];
|
|
|
+ if (velA != null && velE != null) {
|
|
|
+ double? eARatio = ratio(velA, velE).abs();
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.EARatio: eARatio,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else if (!envStart.isNaN &&
|
|
|
+ !envEnd.isNaN &&
|
|
|
+ !envStart.almostEquals(double.minPositive, 0.000001) &&
|
|
|
+ !envEnd.almostEquals(double.minPositive, 0.000001)) {
|
|
|
+ // List<DPoint> maxTraceLine = [];
|
|
|
+ // List<DPoint> meanTraceLine = [];
|
|
|
+ Map<String, double> meanValues = tryCalculateByTrace(
|
|
|
+ maxTraceLine!.map((e) => DPoint(e.x, e.y * 0.75)).toList(),
|
|
|
+ envStart,
|
|
|
+ envEnd,
|
|
|
+ false,
|
|
|
+ );
|
|
|
+ Map<String, double> maxValues = tryCalculateByTrace(
|
|
|
+ maxTraceLine,
|
|
|
+ envStart,
|
|
|
+ envEnd,
|
|
|
+ true,
|
|
|
+ );
|
|
|
+ baseValues.addAll(meanValues);
|
|
|
+ baseValues.addAll(maxValues);
|
|
|
+ }
|
|
|
+
|
|
|
+ double? at = baseValues[MeasureTerms.AT];
|
|
|
+ double? dt = baseValues[MeasureTerms.DT];
|
|
|
+ double? et = baseValues[MeasureTerms.TiEnv];
|
|
|
+
|
|
|
+ if (at != null && dt != null && et != null) {
|
|
|
+ double? atdt = ratio(at, dt).abs();
|
|
|
+ double? atet = ratio(at, et).abs();
|
|
|
+ baseValues.addAll({
|
|
|
+ MeasureTerms.ATDTRatio: atdt,
|
|
|
+ MeasureTerms.ATETRatio: atet,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return baseValues;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, double> tryCalculateByTrace(
|
|
|
+ List<DPoint> traceLine,
|
|
|
+ double envStart,
|
|
|
+ double envEnd,
|
|
|
+ bool isMax,
|
|
|
+ ) {
|
|
|
+ double vti = double.nan;
|
|
|
+ double tiEnv = double.nan;
|
|
|
+ double ta = double.nan;
|
|
|
+ double pv = double.nan;
|
|
|
+ double meanPG = double.nan;
|
|
|
+
|
|
|
+ if (traceLine.isNotEmpty) {
|
|
|
+ List<DPoint> line = [];
|
|
|
+ for (var point in traceLine) {
|
|
|
+ line.add(point);
|
|
|
+ // if (point.x.almostNotLessThan(point.x, envStart) &&
|
|
|
+ // point.x.almostNotGreaterThan(point.x, envEnd)) {
|
|
|
+ // line.add(point);
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ if (line.isNotEmpty) {
|
|
|
+ List<double> vueResult = GeneralFormulas.countVTI(line);
|
|
|
+ vti = vueResult.first;
|
|
|
+ var isShowAbsValue = true; // 假设这是从某个服务获取的值
|
|
|
+ if (isShowAbsValue) {
|
|
|
+ ta = vueResult[2].abs();
|
|
|
+ pv = vueResult[3].abs();
|
|
|
+ tiEnv = vueResult[1];
|
|
|
+ meanPG = vueResult[4];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isMax) {
|
|
|
+ return {
|
|
|
+ MeasureTerms.TAMAX: ta,
|
|
|
+ MeasureTerms.VTI: vti,
|
|
|
+ MeasureTerms.MPG: meanPG,
|
|
|
+ MeasureTerms.TiEnv: tiEnv,
|
|
|
+ MeasureTerms.VelocityMax: pv
|
|
|
+ };
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ MeasureTerms.TAMEAN: ta,
|
|
|
+ MeasureTerms.VTIMean: vti,
|
|
|
+ MeasureTerms.MMPG: meanPG,
|
|
|
+ MeasureTerms.TiEnv: tiEnv,
|
|
|
+ MeasureTerms.VelocityMax: pv,
|
|
|
+ MeasureTerms.PeakPG: pv,
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension DoubleExtensions on double {
|
|
|
+ bool almostEquals(double other, double precision) {
|
|
|
+ if (isNaN && other.isNaN) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return (this - other).abs() <= precision.abs();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool almostNotLessThan(double double1, double double2,
|
|
|
+ [double precision = 0.000001]) {
|
|
|
+ if (double1 < double2) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return double1.almostEquals(double2, precision);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool almostNotGreaterThan(double double1, double double2,
|
|
|
+ [double precision = 0.000001]) {
|
|
|
+ if (double1 > double2) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return double1.almostEquals(double2, precision);
|
|
|
+ }
|
|
|
+}
|