浏览代码

fix 18974: 【图像测量】【小动物】播放视频图像,选中辛普森测量项测量时,计算项测量完毕后,点击下一帧或者上一帧,测量记录还是保留

Melon 9 月之前
父节点
当前提交
25da7c2c97

+ 346 - 0
lib/process/calcuators/simpson.dart

@@ -1,12 +1,19 @@
 // ignore_for_file: non_constant_identifier_names
 
+import 'package:fis_measure/configs/patient.dart';
+import 'package:fis_measure/interfaces/process/calculators/values.dart';
 import 'package:fis_measure/interfaces/process/items/item_metas.dart';
 import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/calcuators/unit_formulas/base/utils.dart';
+import 'package:fis_measure/process/calcuators/unit_formulas/index.dart';
+import 'package:fis_measure/process/items/item_feature.dart';
+import 'package:fis_measure/process/items/top_item.dart';
 import 'package:fis_measure/process/primitives/combos/simpson.dart';
 import 'package:fis_measure/process/primitives/multi_method/multi_simpson_path.dart';
 import 'package:fis_measure/process/primitives/simpson_path.dart';
 import 'package:vid/us/vid_us_unit.dart';
 
+import 'calculator.dart';
 import 'formulas/cardiac.dart';
 import 'lv_study.dart';
 import 'dart:math' as math;
@@ -402,3 +409,342 @@ abstract class _SimpsonFormulas {
     return value;
   }
 }
+
+class LvSimpsonCalNew extends LvSimpsonCalBase<LvStudySimpson> {
+  LvSimpsonCalNew(super.ref) {
+    //
+  }
+
+  @override
+  void calculate() {
+    super.calculate();
+
+    if (ref.feature == null) return;
+
+    final feature = ref.feature!;
+
+    feature.updateStringValue(
+      ItemOutputMeta(
+          ref.meta.description, ref.meta.description, VidUsUnit.None),
+      "",
+    );
+
+    for (var childItem in ref.childItems) {
+      clacChildItem(childItem as MultiSimpsonPath);
+    }
+
+    calcCouple();
+    calcA2c(ref.a2cLvedv, ref.a2cLvesv);
+    calcA2c(ref.a4cLvedv, ref.a4cLvesv);
+
+    doOutput();
+  }
+
+  void calcCouple() {
+    final featureA2cD = findChildFeature(ref.a2cLvedv);
+    final featureA2cS = findChildFeature(ref.a2cLvesv);
+    final featureA4cD = findChildFeature(ref.a4cLvedv);
+    final featureA4cS = findChildFeature(ref.a4cLvesv);
+
+    UnitFloatValue? edv;
+    UnitFloatValue? esv;
+
+    if (featureA2cD != null && featureA4cD != null) {
+      edv = coupleLVEDV(
+        featureA4cD as SimpsonPathFeature,
+        featureA2cD as SimpsonPathFeature,
+      );
+    }
+    if (featureA2cS != null && featureA4cS != null) {
+      esv = coupleLVEDV(
+        featureA4cS as SimpsonPathFeature,
+        featureA2cS as SimpsonPathFeature,
+      );
+    }
+    v.coupleEDV = edv;
+    v.coupleESV = esv;
+    if (edv != null && esv != null) {
+      v.coupleSV = calcSv(edv, esv);
+      v.coupleEF = calcEf(edv, esv);
+      v.coupleCO = calcCo(edv, esv);
+    }
+  }
+}
+
+class LvSingleSimpsonCalNew extends LvSimpsonCalBase<LvStudySingleSimpson> {
+  LvSingleSimpsonCalNew(super.ref) {
+    //
+  }
+
+  @override
+  void calculate() {
+    super.calculate();
+
+    if (ref.feature == null) return;
+
+    final feature = ref.feature!;
+
+    feature.updateStringValue(
+      ItemOutputMeta(
+          ref.meta.description, ref.meta.description, VidUsUnit.None),
+      "",
+    );
+
+    for (var childItem in ref.childItems) {
+      clacChildItem(childItem as MultiSimpsonPath);
+    }
+
+    if (ref.meta.description.toLowerCase().contains("a2c")) {
+      calcA2c(ref.lvedv, ref.lvesv);
+    }
+
+    if (ref.meta.description.toLowerCase().contains("a4c")) {
+      calcA4c(ref.lvedv, ref.lvesv);
+    }
+
+    doOutput();
+  }
+}
+
+abstract class LvSimpsonCalBase<T extends TopMeasureItem>
+    extends Calculator<T, double> {
+  LvSimpsonCalBase(super.ref);
+
+  _SimpsonValues v = _SimpsonValues();
+
+  @override
+  void calculate() {
+    v = _SimpsonValues();
+  }
+
+  void doOutput() {
+    final feature = ref.feature!;
+
+    for (var output in ref.meta.outputs) {
+      switch (output.name) {
+        // Couple
+        case MeasureTerms.LVEDV:
+          _outputVal(feature, output, v.coupleEDV);
+          break;
+        case MeasureTerms.LVESV:
+          _outputVal(feature, output, v.coupleESV);
+          break;
+        case MeasureTerms.SV:
+          _outputVal(feature, output, v.coupleSV);
+          break;
+        case MeasureTerms.EF:
+          _outputVal(feature, output, v.coupleEF);
+          break;
+        case MeasureTerms.CO:
+          _outputVal(feature, output, v.coupleCO);
+          break;
+        // A2C
+        case "A2C LVEDV":
+          _outputVal(feature, output, v.a2cEDV);
+          break;
+        case "A2C LVESV":
+          _outputVal(feature, output, v.a2cESV);
+          break;
+        case "A2C SV":
+          _outputVal(feature, output, v.a2cSV);
+          break;
+        case "A2C EF":
+          _outputVal(feature, output, v.a2cEF);
+          break;
+        case "A2C CO":
+          _outputVal(feature, output, v.a2cCO);
+          break;
+        // A4C
+        case "A4C LVEDV":
+          _outputVal(feature, output, v.a4cEDV);
+          break;
+        case "A4C LVESV":
+          _outputVal(feature, output, v.a4cESV);
+          break;
+        case "A4C SV":
+          _outputVal(feature, output, v.a4cSV);
+          break;
+        case "A4C EF":
+          _outputVal(feature, output, v.a4cEF);
+          break;
+        case "A4C CO":
+          _outputVal(feature, output, v.a4cCO);
+          break;
+        default:
+      }
+    }
+  }
+
+  void _outputVal(
+    MeasureItemFeature feature,
+    ItemOutputMeta output,
+    UnitFloatValue? val,
+  ) {
+    if (val != null) {
+      feature.updateFloatValue(output, val.value, val.unit);
+    }
+  }
+
+  void calcA2c(MultiSimpsonPath itemD, MultiSimpsonPath itemS) {
+    final featureD = findChildFeature(itemD);
+    final featureS = findChildFeature(itemS);
+    UnitFloatValue? edv;
+    UnitFloatValue? esv;
+    if (featureD != null) {
+      edv = singleLVEDV(featureD as SimpsonPathFeature);
+    }
+    if (featureS != null) {
+      esv = singleLVEDV(featureS as SimpsonPathFeature);
+    }
+    v.a2cEDV = edv;
+    v.a2cESV = esv;
+    if (edv != null && esv != null) {
+      v.a2cSV = calcSv(edv, esv);
+      v.a2cEF = calcEf(edv, esv);
+      v.a2cCO = calcCo(edv, esv);
+    }
+  }
+
+  void calcA4c(MultiSimpsonPath itemD, MultiSimpsonPath itemS) {
+    final featureD = findChildFeature(itemD);
+    final featureS = findChildFeature(itemS);
+    UnitFloatValue? edv;
+    UnitFloatValue? esv;
+    if (featureD != null) {
+      edv = singleLVEDV(featureD as SimpsonPathFeature);
+    }
+    if (featureS != null) {
+      esv = singleLVEDV(featureS as SimpsonPathFeature);
+    }
+    v.a4cEDV = edv;
+    v.a4cESV = esv;
+    if (edv != null && esv != null) {
+      v.a4cSV = calcSv(edv, esv);
+      v.a4cEF = calcEf(edv, esv);
+      v.a4cCO = calcCo(edv, esv);
+    }
+  }
+
+  UnitFloatValue? coupleLVEDV(
+    SimpsonPathFeature featureA,
+    SimpsonPathFeature featureB,
+  ) {
+    // max([A4C LVEDV Simpson|L"cm;2D"],[A2C LVEDV Simpson|L"cm;2D"]) *3.1415926/(4*20) * Sum(2D1*4D1 + 2D2*4D2 + ... + 2D20*4D20)
+    double lA = roundDouble(featureA.centerLineLength);
+    double lB = roundDouble(featureB.centerLineLength);
+    double lMax = math.max(lA, lB);
+
+    final splitterALengths = featureA.horizontalSplitterLegths.values.toList();
+    final splitterBLengths = featureB.horizontalSplitterLegths.values.toList();
+
+    if (splitterALengths.length < SimpsonPath.splitterCount ||
+        splitterBLengths.length < SimpsonPath.splitterCount) {
+      return null;
+    }
+
+    double sum = 0;
+    for (var i = 0; i < SimpsonPath.splitterCount; i++) {
+      final dA = roundDouble(splitterALengths[i]);
+      final dB = roundDouble(splitterBLengths[i]);
+      sum += dA * dB;
+    }
+
+    var value = lMax * 3.1415926 / (4 * 20) * sum;
+    value = value * 4;
+    return UnitFloatValue(value, VidUsUnit.ml);
+  }
+
+  UnitFloatValue? singleLVEDV(SimpsonPathFeature feature) {
+    // [A2C LVEDV Simpson|L"cm;2D"]*3.1415926/(4*20)* Sum(pow(D1,2),pow(D2,2),...,pow(D20,2))
+    double l = roundDouble(feature.centerLineLength);
+
+    final splitterLengths = feature.horizontalSplitterLegths.values.toList();
+
+    if (splitterLengths.length < SimpsonPath.splitterCount) {
+      return null;
+    }
+
+    double sum = 0;
+    for (var i = 0; i < SimpsonPath.splitterCount; i++) {
+      final d = math.pow(splitterLengths[i], 2).toDouble();
+      sum += roundDouble(d);
+    }
+    var value = l * 3.1415926 / (4 * 20) * sum;
+    return UnitFloatValue(value, VidUsUnit.ml);
+  }
+
+  UnitFloatValue calcSv(UnitFloatValue lvedv, UnitFloatValue lvesv) {
+    // [LVEDV(A2C Simp)"ml;"]-[LVESV(A2C Simp)"ml;"]
+    final value = lvedv.convert(VidUsUnit.ml) - lvesv.convert(VidUsUnit.ml);
+    return UnitFloatValue(value, VidUsUnit.ml);
+  }
+
+  UnitFloatValue calcEf(UnitFloatValue lvedv, UnitFloatValue lvesv) {
+    // ([LVEDV(A2C Simp)"ml;"]-[LVESV(A2C Simp)"ml;"])/[LVEDV(A2C Simp)"ml;"]*100
+    final value = UnitFormulas.cardiac.ef(lvedv, lvesv);
+    return value;
+  }
+
+  UnitFloatValue? calcCo(UnitFloatValue lvedv, UnitFloatValue lvesv) {
+    // ([LVEDV(A2C Simp)"ml;"]-[LVESV(A2C Simp)"ml;"])*{[HR|HR"HR;M"]}/1000
+    // ([LVEDV(A2C Simp)"ml;"]-[LVESV(A2C Simp)"ml;"])*{[HR|HR"HR;M"],[HR"HR;PatientInfo"]}/1000
+    final hr = GlobalPatientConfig.hr;
+    if (hr == null) {
+      return null;
+    }
+    final edv = lvedv.convert(VidUsUnit.ml);
+    final esv = lvesv.convert(VidUsUnit.ml);
+    final value = (edv - esv) * hr / 1000;
+    return UnitFloatValue(value, VidUsUnit.Lmin);
+  }
+
+  void clacChildItem(MultiSimpsonPath childItem) {
+    final feature = findChildFeature(childItem);
+    if (feature == null) {
+      return;
+    }
+    feature as SimpsonPathFeature;
+
+    // final outputs = childItem.meta.outputs;
+    // TODO:
+    final itemName = childItem.meta.name.split(' ').first;
+    final isD = itemName.toLowerCase().contains("edv");
+    final sideTag = isD ? "d" : "s";
+    final outputs = [
+      ItemOutputMeta("Area", "$itemName LVA$sideTag", VidUsUnit.mm2),
+      ItemOutputMeta("L", "$itemName LVL$sideTag", VidUsUnit.mm),
+    ];
+    for (var output in outputs) {
+      switch (output.name) {
+        case "Area":
+          feature.updateFloatValue(output, feature.area, VidUsUnit.cm2);
+          break;
+        case "L":
+          feature.updateFloatValue(
+              output, feature.centerLineLength, VidUsUnit.cm);
+          break;
+        default:
+      }
+    }
+  }
+}
+
+class _SimpsonValues {
+  UnitFloatValue? a2cEDV;
+  UnitFloatValue? a2cESV;
+  UnitFloatValue? a2cSV;
+  UnitFloatValue? a2cEF;
+  UnitFloatValue? a2cCO;
+
+  UnitFloatValue? a4cEDV;
+  UnitFloatValue? a4cESV;
+  UnitFloatValue? a4cSV;
+  UnitFloatValue? a4cEF;
+  UnitFloatValue? a4cCO;
+
+  UnitFloatValue? coupleEDV;
+  UnitFloatValue? coupleESV;
+  UnitFloatValue? coupleSV;
+  UnitFloatValue? coupleEF;
+  UnitFloatValue? coupleCO;
+}

+ 3 - 3
lib/process/items/item.dart

@@ -232,9 +232,9 @@ abstract class MeasureItem<T extends MeasureItemFeature> extends IMeasureItem {
   }
 
   void _recordCrossFrameIndex(int frameIndex) {
-    if (parent != null) {
-      final parentItem = parent! as TopMeasureItem;
-      if (parentItem.isCrossFrameMode) {
+    final canRecord = parent != null || !(this is ITopMeasureItem);
+    if (canRecord) {
+      if (application.crossFrameContext != null) {
         application.crossFrameContext!.recordFrame(frameIndex);
         application.crossFrameAdded.emit(this, frameIndex);
       }

+ 4 - 0
lib/process/items/item_feature.dart

@@ -289,6 +289,10 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
 
   @override
   bool checkCanPaint() {
+    if (refItem is ITopMeasureItem) {
+      return true;
+    }
+
     bool result = true;
     final app = refItem.application;
     if (app.crossFrameContext != null && !app.crossFrameContext!.isOver) {

+ 8 - 2
lib/process/primitives/combos/simpson.dart

@@ -37,12 +37,15 @@ class LvStudySimpson extends TopMeasureItem<LvStudySimpsonFeature> {
   @override
   bool get finishAfterUnactive => true;
 
+  @override
+  bool get isCrossFrameMode => true;
+
   @override
   LvStudySimpsonFeature buildFeature() => LvStudySimpsonFeature(this);
 
   static LvStudySimpson create(ItemMeta meta, [IMeasureItem? parent]) {
     final simpson = LvStudySimpson(meta);
-    simpson.calculator = LvSimpsonCal(simpson);
+    simpson.calculator = LvSimpsonCalNew(simpson);
     return simpson;
   }
 }
@@ -66,13 +69,16 @@ class LvStudySingleSimpson extends TopMeasureItem<LvStudySingleSimpsonFeature> {
   @override
   bool get finishAfterUnactive => true;
 
+  @override
+  bool get isCrossFrameMode => true;
+
   @override
   LvStudySingleSimpsonFeature buildFeature() =>
       LvStudySingleSimpsonFeature(this);
 
   static LvStudySingleSimpson create(ItemMeta meta, [IMeasureItem? parent]) {
     final simpson = LvStudySingleSimpson(meta);
-    simpson.calculator = LvSingleSimpsonCal(simpson);
+    simpson.calculator = LvSingleSimpsonCalNew(simpson);
     return simpson;
   }
 }