123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- import 'package:fis_measure/interfaces/date_types/point.dart';
- import 'package:fis_measure/interfaces/date_types/vector.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/dop_trace_disp/cardiac_cycle.dart';
- import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
- import 'dart:math' as math;
- import 'calculator.dart';
- class TraceCal extends Calculator<TraceItemAbstract, double> {
- TraceCal(TraceItemAbstract ref) : super(ref);
- @override
- void calculate() {
- if (ref.feature == null) return;
- final feature = ref.feature!;
- final viewport = feature.hostVisualArea!.viewport!;
- // 加入画布偏移量
- final canvasOffset = feature.hostVisualArea!.layoutRegion!.topLeft;
- //加入坐标系偏移量
- final coordinateOffset = viewport.region;
- final regionPoints = feature.innerPoints
- .map((e) => viewport
- .convert(
- e.clone().addVector(DVector(-canvasOffset.x, -canvasOffset.y)))
- .addVector(DVector(coordinateOffset.left, -coordinateOffset.top)))
- .toList();
- final yFlippedPoints = regionPoints.map((e) => DPoint(e.x, -e.y)).toList();
- double fakeOutputDate = 0;
- var countVTIResult = GeneralFormulas.countVTI(yFlippedPoints);
- Map<String, double> result = calculateDopplerTrace(yFlippedPoints);
- var outputVTI = countVTIResult[0];
- var outputVTIMean = countVTIResult[0];
- var outputTiEnv = countVTIResult[1];
- var outputTAMAX = countVTIResult[2];
- var outputTAMEAN = countVTIResult[2];
- var outputVelocityMax = countVTIResult[3];
- var outputVelocityMean = countVTIResult[3];
- var outputMPG = countVTIResult[4];
- var outputMMPG = countVTIResult[4];
- var outputHR = countVTIResult[5];
- for (var output in ref.meta.outputs) {
- ///TODO:[Gavin] 实现以下计算逻辑
- switch (output.name) {
- case MeasureTerms.TAMAX:
- feature.updateFloatValue(output, outputTAMAX, output.unit);
- break;
- case MeasureTerms.TAMEAN:
- feature.updateFloatValue(output, outputTAMEAN, output.unit);
- break;
- case MeasureTerms.PS:
- if (result[MeasureTerms.PS] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.PS]!, output.unit);
- }
- break;
- case MeasureTerms.ED:
- if (result[MeasureTerms.ED] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.ED]!, output.unit);
- }
- break;
- case MeasureTerms.MD:
- if (result[MeasureTerms.MD] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.MD]!, output.unit);
- }
- break;
- case MeasureTerms.HeartRate:
- feature.updateFloatValue(output, outputHR, output.unit);
- break;
- case MeasureTerms.Acceleration:
- if (result[MeasureTerms.Acceleration] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.Acceleration]!, output.unit);
- }
- break;
- case MeasureTerms.AT:
- if (result[MeasureTerms.AT] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.AT]!, output.unit);
- }
- break;
- case MeasureTerms.PSED:
- if (result[MeasureTerms.PSED] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.PSED]!, output.unit);
- }
- break;
- case MeasureTerms.EDPS:
- if (result[MeasureTerms.EDPS] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.EDPS]!, output.unit);
- }
- break;
- case MeasureTerms.PI:
- if (result[MeasureTerms.PI] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.PI]!, output.unit);
- }
- break;
- case MeasureTerms.PIMD:
- if (result[MeasureTerms.PIMD] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.PIMD]!, output.unit);
- }
- break;
- case MeasureTerms.RI:
- if (result[MeasureTerms.RI] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.RI]!, output.unit);
- }
- break;
- case MeasureTerms.RIMD:
- if (result[MeasureTerms.RIMD] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.RIMD]!, output.unit);
- }
- break;
- case MeasureTerms.MaxPG:
- feature.updateFloatValue(
- output, result[MeasureTerms.MaxPG]!, output.unit);
- break;
- case MeasureTerms.VelocityMax:
- feature.updateFloatValue(output, outputVelocityMax, output.unit);
- break;
- case MeasureTerms.VelocityMean:
- feature.updateFloatValue(output, outputVelocityMean, output.unit);
- break;
- case MeasureTerms.PeakPG:
- feature.updateFloatValue(
- output, result[MeasureTerms.PeakPG]!, output.unit);
- break;
- case MeasureTerms.VTI:
- feature.updateFloatValue(output, outputVTI, output.unit);
- break;
- case MeasureTerms.VTIMean:
- feature.updateFloatValue(output, outputVTIMean, output.unit);
- break;
- case MeasureTerms.MPG:
- feature.updateFloatValue(output, outputMPG, output.unit);
- break;
- case MeasureTerms.MMPG:
- feature.updateFloatValue(output, outputMMPG, output.unit);
- break;
- case MeasureTerms.TiEnv:
- // var outputTiEnv = GeneralFormulas.countEnvelopeTime(points);
- feature.updateFloatValue(output, outputTiEnv, output.unit);
- break;
- case MeasureTerms.EVEL:
- if (result[MeasureTerms.EVEL] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.EVEL]!, output.unit);
- }
- break;
- case MeasureTerms.AVEL:
- if (result[MeasureTerms.AVEL] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.AVEL]!, output.unit);
- }
- break;
- case MeasureTerms.EARatio:
- if (result[MeasureTerms.EARatio] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.EARatio]!, output.unit);
- }
- break;
- case MeasureTerms.DT:
- if (result[MeasureTerms.DT] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.DT]!, output.unit);
- }
- break;
- case MeasureTerms.PHT:
- if (result[MeasureTerms.PHT] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.PHT]!, output.unit);
- }
- break;
- case MeasureTerms.VA:
- if (result[MeasureTerms.VA] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.VA]!, output.unit);
- }
- break;
- case MeasureTerms.ADur:
- if (result[MeasureTerms.ADur] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.ADur]!, output.unit);
- }
- break;
- case MeasureTerms.ATDTRatio:
- if (result[MeasureTerms.ATDTRatio] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.ATDTRatio]!, output.unit);
- }
- break;
- case MeasureTerms.ATETRatio:
- if (result[MeasureTerms.ATETRatio] != null) {
- feature.updateFloatValue(
- output, result[MeasureTerms.ATETRatio]!, output.unit);
- }
- break;
- // case MeasureTerms.Trace:
- // feature.updateFloatValue(output, fakeOutputDate, output.unit);
- // break;
- default:
- break;
- }
- }
- }
- static List<DPoint> getFeatureYFlippedPoints(MeasureItemFeature feature) {
- final viewport = feature.hostVisualArea!.viewport!;
- // 加入画布偏移量
- final canvasOffset = feature.hostVisualArea!.layoutRegion!.topLeft;
- //加入坐标系偏移量
- final coordinateOffset = viewport.region;
- final regionPoints = feature.innerPoints
- .map((e) => viewport
- .convert(
- e.clone().addVector(DVector(-canvasOffset.x, -canvasOffset.y)))
- .addVector(DVector(coordinateOffset.left, -coordinateOffset.top)))
- .toList();
- final points = regionPoints.map((e) => DPoint(e.x, -e.y)).toList();
- return points;
- }
- static 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) {
- vtiMean = GeneralFormulas.countVTI(meanTraceLineOfCycle).first;
- }
- 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>? maxTraceLineOfCycle,
- ]) {
- double cyclesStart = double.maxFinite;
- double cyclesEnd = double.minPositive;
- DPoint systoleStart = maxTraceLineOfCycle!.first;
- DPoint diastoleEnd = maxTraceLineOfCycle.last;
- double maxDistance = 0;
- DPoint peakSystolic = DPoint(0, 0);
- for (var i = 1; i < maxTraceLineOfCycle.length; i++) {
- final point = maxTraceLineOfCycle[i];
- final distance = (point.y - systoleStart.y).abs();
- if (distance > maxDistance) {
- maxDistance = distance;
- peakSystolic = point;
- }
- }
- List<CardiacCycle> validSystoleCycles = [
- CardiacCycle(
- diastoleEnd: diastoleEnd,
- systoleStart: systoleStart,
- minimumAbsoluteVelocity: DPoint(0, 0),
- index: 0,
- peakSystolic: peakSystolic,
- )
- ];
- Map<String, double> baseValues = {};
- /// 获取全量的测量值的方法 dopplerTrace.AvgHeartCycle > 0 && dopplerTrace.CardiacCycles.Count >= dopplerTrace.AvgHeartCycle
- for (CardiacCycle i in validSystoleCycles) {
- cyclesStart = math.min(i.systoleStart.x, cyclesStart);
- cyclesEnd = math.max(i.diastoleEnd.x, cyclesEnd);
- /// TODO 这边需要看超声机代码,继续捞值
- // List<DPoint> maxTraceLineOfCycle = [];
- List<DPoint> meanTraceLineOfCycle = [];
- baseValues = calculateHeartCycleRelevantValues(
- i,
- 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,
- });
- }
- 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) {
- 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);
- }
- }
|