Răsfoiți Sursa

part of calctor 4 lvstudy

Melon 1 an în urmă
părinte
comite
9b331806e0

+ 10 - 0
lib/configs/cardiac.dart

@@ -0,0 +1,10 @@
+// ignore_for_file: non_constant_identifier_names
+
+import 'package:fis_measure/interfaces/enums/calcuator.dart';
+
+abstract class GlobalCardiacConfigs {
+  static const density = 1.04;
+  static const correctionFactor = 13.6;
+
+  static CardiacEDVFormulaMode EDVFormulaMode = CardiacEDVFormulaMode.teichholz;
+}

+ 12 - 0
lib/configs/patient.dart

@@ -0,0 +1,12 @@
+// ignore_for_file: non_constant_identifier_names
+
+import 'dart:math' as math;
+
+abstract class GlobalPatientConfig {
+  /// 人体表面积
+  ///
+  /// - Mosteller 公式:一个最经常使用的公式,发布于1987年。BSA (m²) = ( [身高(cm) x 体重(kg) ]/ 3600 )^½。
+  ///
+  /// - https://blog.csdn.net/qq_15560295/article/details/105025073
+  static double BSA = math.sqrt((170.0 * 70.0) / 3600);
+}

+ 4 - 0
lib/interfaces/enums/calcuator.dart

@@ -0,0 +1,4 @@
+enum CardiacEDVFormulaMode {
+  cube,
+  teichholz,
+}

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

@@ -84,4 +84,7 @@ abstract class ITopMeasureItem implements IMeasureItem {
 
   /// 切换制定子项为工作状态
   void switchChild(int index);
+
+  /// 根据名称获取子项
+  IMeasureItem findChildByName(String name);
 }

+ 26 - 0
lib/interfaces/process/workspace/cross_frame.dart

@@ -0,0 +1,26 @@
+import 'package:fis_common/event/event_type.dart';
+import 'package:vid/us/vid_us_image.dart';
+
+/// 跨帧贮存器
+abstract class ICrossFrameStore {
+  /// 原始帧
+  VidUsImage? get originFrame;
+
+  /// 附帧
+  VidUsImage? get subFrame;
+
+  /// 原始帧索引
+  int get originIndex;
+
+  /// 附帧索引
+  int get subIndex;
+
+  /// 帧还原 事件
+  late final FEventHandler<int> frameRestored;
+
+  /// 恢复
+  Future<void> restore();
+
+  /// 跨到指定帧
+  Future<void> crossTo(VidUsImage? frame);
+}

+ 59 - 17
lib/process/calcuators/formulas/cardiac.dart

@@ -5,12 +5,11 @@ import 'package:fis_measure/utils/number.dart';
 class CardiacFormulas {
   CardiacFormulas._();
 
-  /// <summary>
-  /// IMP=(CO-ET)/ET
-  /// </summary>
-  /// <param name="co">s</param>
-  /// <param name="et">s</param>
-  /// <returns>(unit None)</returns>
+  /// IMP
+  ///
+  /// Formula: `(CO-ET)/ET`
+  ///
+  /// Result Unit: `None`
   static double teiIndex(double co, double et) {
     double imp = 0.0;
     if (et != 0.0) {
@@ -19,12 +18,14 @@ class CardiacFormulas {
     return imp;
   }
 
-  /// <summary>
-  /// EF = (EDV - ESV )/EDV
-  /// </summary>
-  /// <param name="edv">cm³</param>
-  /// <param name="esv">cm³</param>
-  /// <returns>None</returns>
+  /// EF
+  /// Formula: `(EDV - ESV )/EDV`
+  ///
+  /// [edv] Unit: `cm³`
+  ///
+  /// [esv] Unit: `cm³`
+  ///
+  /// Result Unit: `None`
   static double ef(double edv, double esv) {
     // 这行判断暂时注释掉是为了使实际表现与旧版一致
     // if (edv < esv) {
@@ -33,11 +34,13 @@ class CardiacFormulas {
     return (edv - esv) / edv * 100;
   }
 
-  /// <summary>
-  /// EDV (Teichholz) = [7.0/(2.4 + LVIDd)] x LVIDd^3
-  /// </summary>
-  /// <param name="lvidd">Unit cm</param>
-  /// <returns>cm³</returns>
+  /// EDV (Teichholz)
+  ///
+  ///  Formula: `[7.0/(2.4 + LVIDd)] x LVIDd^3`
+  ///
+  /// [lvidd] Unit: `cm`
+  ///
+  /// Result Unit: `cm³`
   static double edvTeichholz(double lvidd) {
     double edv = double.nan;
     if (!NumUtil.almostEquals(lvidd, 0)) {
@@ -45,4 +48,43 @@ class CardiacFormulas {
     }
     return edv;
   }
+
+  /// EDV (Cube)
+  ///
+  ///  Formula: `LVIDd^3`
+  ///
+  /// [lvidd] Unit: `cm`
+  ///
+  /// Result Unit: `cm³`
+  static double edvCube(double lvidd) {
+    double edv = double.nan;
+    if (!NumUtil.almostEquals(lvidd, 0)) {
+      edv = math.pow(lvidd, 3).toDouble();
+    }
+    return edv;
+  }
+
+  /// ESV (Teichholz)
+  ///
+  ///  Formula: `[7.0/(2.4 + LVIDs)] x LVIDs^3`
+  ///
+  /// [lvids] Unit: `cm`
+  ///
+  /// Result Unit: `cm³`
+  static double esvTeichholz(double lvids) {
+    // 计算公式相同,入参不同
+    return edvTeichholz(lvids);
+  }
+
+  /// ESV (Cube)
+  ///
+  ///  Formula: `LVIDs^3`
+  ///
+  /// [lvids] Unit: `cm`
+  ///
+  /// Result Unit: `cm³`
+  static double esvCube(double lvids) {
+    // 计算公式相同,入参不同
+    return edvCube(lvids);
+  }
 }

+ 212 - 0
lib/process/calcuators/lv_study.dart

@@ -0,0 +1,212 @@
+import 'package:fis_measure/configs/cardiac.dart';
+import 'package:fis_measure/configs/patient.dart';
+import 'package:fis_measure/interfaces/enums/calcuator.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/primitives/combos/straightline_group.dart';
+import 'package:fis_measure/process/primitives/straightline.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+import 'calculator.dart';
+import 'formulas/cardiac.dart';
+
+class LvStudyDistanceGroupCal extends Calculator<StraightLineGroup, double> {
+  late final StraightLine kidIVSd;
+  late final StraightLine kidLVIDd;
+  late final StraightLine kidLVPWd;
+  late final StraightLine kidIVSs;
+  late final StraightLine kidLVIDs;
+  late final StraightLine kidLVPWs;
+  late _ValTemp _v;
+
+  LvStudyDistanceGroupCal(super.ref) {
+    kidIVSd = ref.findChildByName(MeasureTerms.IVSd) as StraightLine;
+    kidLVIDd = ref.findChildByName(MeasureTerms.LVIDd) as StraightLine;
+    kidLVPWd = ref.findChildByName(MeasureTerms.LVPWd) as StraightLine;
+    kidIVSs = ref.findChildByName(MeasureTerms.IVSs) as StraightLine;
+    kidLVIDs = ref.findChildByName(MeasureTerms.LVIDs) as StraightLine;
+    kidLVPWs = ref.findChildByName(MeasureTerms.LVPWs) as StraightLine;
+  }
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final feature = ref.feature!;
+    // feature.updateStringValue(ref.meta.outputs.first, "");
+
+    _v = _ValTemp();
+    _v.ivsd = pickChildFloatValue(kidIVSd);
+    _v.ivss = pickChildFloatValue(kidIVSs);
+    _v.lvidd = pickChildFloatValue(kidLVIDd);
+    _v.lvids = pickChildFloatValue(kidLVIDs);
+    _v.lvpwd = pickChildFloatValue(kidLVPWd);
+    _v.lvpws = pickChildFloatValue(kidLVPWs);
+
+    for (var output in ref.meta.outputs) {
+      switch (output.name) {
+        case MeasureTerms.LvStudy:
+          feature.updateStringValue(output, "");
+          break;
+        case MeasureTerms.LVEDV:
+          _updateLVEDV();
+          break;
+        case MeasureTerms.LVESV:
+          _updateLVESV();
+          break;
+      }
+    }
+
+    // if (ivsd != null && ivss != null) {
+    //   _updatePercentIVS(ivss, ivsd);
+    // }
+
+    // if (lvidd != null && lvids != null) {
+    //   _updateSV();
+    //   _updateEF();
+
+    //   _updatePercentFS(lvidd, lvids);
+    // }
+
+    // if (lvpws != null && lvpwd != null) {
+    //   _updatePercentLVPW(lvpws, lvpwd);
+    // }
+
+    // if (ivsd != null && lvidd != null && lvpwd != null) {
+    //   _updateLVdMass(ivsd, ivsd, lvpwd);
+    // }
+
+    ref.feature!.values;
+    // ref.meta.outputs
+
+    // updateStringValue("");
+    // return;
+  }
+
+  void _updateLVEDV() {
+    if (_v.lvidd == null) {
+      return;
+    }
+    final lvidd = _v.lvidd!;
+
+    double value = 0;
+    if (GlobalCardiacConfigs.EDVFormulaMode ==
+        CardiacEDVFormulaMode.teichholz) {
+      value = CardiacFormulas.edvTeichholz(lvidd);
+    } else if (GlobalCardiacConfigs.EDVFormulaMode ==
+        CardiacEDVFormulaMode.cube) {
+      value = CardiacFormulas.edvCube(lvidd);
+    }
+    _updateFloatValueByName(MeasureTerms.LVEDV, value, unit: VidUsUnit.cm3);
+    _v.lvedv = value;
+  }
+
+  void _updateLVESV() {
+    if (_v.lvidd == null) {
+      return;
+    }
+    final lvids = _v.lvids!;
+
+    double value = 0;
+    if (GlobalCardiacConfigs.EDVFormulaMode ==
+        CardiacEDVFormulaMode.teichholz) {
+      value = CardiacFormulas.esvTeichholz(lvids);
+    } else if (GlobalCardiacConfigs.EDVFormulaMode ==
+        CardiacEDVFormulaMode.cube) {
+      value = CardiacFormulas.esvCube(lvids);
+    }
+    _updateFloatValueByName(MeasureTerms.LVESV, value, unit: VidUsUnit.cm3);
+    _v.lvesv = value;
+  }
+
+  void _updateSV() {
+    if (_v.lvedv == null || _v.lvesv == null) {
+      return;
+    }
+    final edv = _v.lvedv!;
+    final esv = _v.lvesv!;
+    double value = edv - esv;
+    _updateFloatValueByName(MeasureTerms.SV, value);
+    _v.sv = value;
+  }
+
+  void _updateEF() {
+    if (_v.lvedv == null || _v.lvesv == null) {
+      return;
+    }
+    final edv = _v.lvedv!;
+    final esv = _v.lvesv!;
+    double value = ((edv - esv) / edv) * 100;
+    _updateFloatValueByName(MeasureTerms.EF, value);
+    _v.sv = value;
+  }
+
+  void _updatePercentFS(double lvidd, double lvids) {
+    double value = ((lvidd - lvids) / lvidd) * 100;
+    _updateFloatValueByName(MeasureTerms.FS, value);
+    _v.sv = value;
+  }
+
+  void _updatePercentIVS(double ivss, double ivsd) {
+    double value = ((ivss - ivsd) / ivsd) * 100;
+    _updateFloatValueByName(MeasureTerms.PercentIVS, value);
+    _v.sv = value;
+  }
+
+  void _updatePercentLVPW(double lvpws, double lvpwd) {
+    double value = ((lvpws - lvpwd) / lvpwd) * 100;
+    _updateFloatValueByName(MeasureTerms.PercentLVPW, value);
+    _v.sv = value;
+  }
+
+  void _updateLVdMass(double ivsd, double lvidd, double lvpwd) {
+    const density = GlobalCardiacConfigs.density;
+    const correctionFactor = GlobalCardiacConfigs.correctionFactor;
+    double part1 = math.pow(ivsd + lvidd + lvpwd, 3).toDouble();
+    double part2 = math.pow(lvidd, 3).toDouble();
+    double value = ((density * part1 - part2) + correctionFactor) / 1000.0;
+    _updateFloatValueByName(MeasureTerms.LVdMass, value);
+    _v.sv = value;
+  }
+
+  void _updateCO() {
+    if (_v.sv == null || _v.hr == null) {
+      return;
+    }
+    double value = (_v.sv! - _v.hr!) / 1000.0;
+    _updateFloatValueByName(MeasureTerms.CO, value);
+  }
+
+  void _updateCI() {
+    if (_v.sv == null || _v.hr == null) {
+      return;
+    }
+    double value = ((_v.sv! - _v.hr!) / 1000.0) / GlobalPatientConfig.BSA;
+    _updateFloatValueByName(MeasureTerms.CI, value);
+  }
+
+  void _updateFloatValueByName(
+    String name,
+    double value, {
+    VidUsUnit? unit,
+  }) {
+    ref.measuredFeatures;
+    final feature = ref.feature!;
+    final outputMeta = ref.meta.outputs.firstWhere((x) => x.name == name);
+    feature.updateFloatValue(outputMeta, value, unit ?? outputMeta.unit);
+  }
+}
+
+class _ValTemp {
+  double? ivsd = 0.0;
+  double? lvidd = 0.0;
+  double? lvpwd = 0.0;
+  double? ivss = 0.0;
+  double? lvids = 0.0;
+  double? lvpws = 0.0;
+
+  double? lvedv = 0.0;
+  double? lvesv = 0.0;
+  double? sv = 0.0;
+  double? hr = 0.0;
+}

+ 3 - 2
lib/process/items/factory.dart

@@ -6,8 +6,8 @@ import 'package:fis_measure/process/primitives/carotid_imt.dart';
 import 'package:fis_measure/process/primitives/combos/afi.dart';
 import 'package:fis_measure/process/primitives/combos/area_straightline.dart';
 import 'package:fis_measure/process/primitives/combos/depth2baseline.dart';
-import 'package:fis_measure/process/primitives/combos/lv_test.dart';
 import 'package:fis_measure/process/primitives/combos/lwh_straightline.dart';
+import 'package:fis_measure/process/primitives/combos/straightline_group.dart';
 import 'package:fis_measure/process/primitives/combos/three_ray.dart';
 import 'package:fis_measure/process/primitives/combos/two_area.dart';
 import 'package:fis_measure/process/primitives/combos/two_location.dart';
@@ -81,7 +81,8 @@ class MeasureItemFactory {
   }
 
   static void _registerItemCreators() {
-    _singleton._register(MeasureTypes.LvGroupByLineGroup, LvTest.crateTest);
+    _singleton._register(
+        MeasureTypes.LvGroupByLineGroup, StraightLineGroup.createLvStudy);
 
     // Empty
     _singleton._register(MeasureTypes.Empty, Empty.createEmpty);

+ 10 - 0
lib/process/items/top_item.dart

@@ -56,6 +56,16 @@ abstract class TopMeasureItem<T extends MeasureItemFeature>
     workingChildChanged.emit(this, index);
   }
 
+  @override
+  IMeasureItem findChildByName(String name) {
+    try {
+      final item = _childItems.firstWhere((e) => e.meta.name == name);
+      return item;
+    } catch (e) {
+      throw ArgumentError("Child item not found.", name);
+    }
+  }
+
   T buildFeature();
 
   @override

+ 0 - 85
lib/process/primitives/combos/lv_test.dart

@@ -1,85 +0,0 @@
-import 'dart:ui';
-
-import 'package:fis_measure/interfaces/process/items/item.dart';
-import 'package:fis_measure/interfaces/process/items/item_metas.dart';
-import 'package:fis_measure/interfaces/process/items/types.dart';
-import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
-import 'package:fis_measure/process/calcuators/a_b_ratio.dart';
-import 'package:fis_measure/process/calcuators/calculator.dart';
-import 'package:fis_measure/process/calcuators/stenosis.dart';
-import 'package:fis_measure/process/calcuators/three_distance.dart';
-import 'package:fis_measure/process/calcuators/two_distance.dart';
-import 'package:fis_measure/process/items/item_feature.dart';
-import 'package:fis_measure/process/items/top_item.dart';
-import 'package:fis_measure/process/items/top_item_feature.dart';
-import 'package:fis_measure/process/primitives/straightline.dart';
-import 'package:fis_measure/process/primitives/trace.dart';
-
-import 'two_length.dart';
-
-class LvTest extends TwoLengthAbstract<LvTestFeature> {
-  static const String _area1Key = "D1";
-  static const String _area2Key = "D2";
-
-  late final StraightLine a1;
-  late final StraightLine a2;
-
-  LvTest(ItemMeta meta) : super(meta) {
-    final metaA1 = meta.getChildByName(_area1Key)!;
-    final metaA2 = meta.getChildByName(_area2Key)!;
-    a1 = StraightLine.createDistance(metaA1, this);
-    a2 = StraightLine.createDistance(metaA2, this);
-    childItems.add(a1);
-    childItems.add(a2);
-  }
-
-  @override
-  StraightLine get child1 => a1;
-
-  @override
-  StraightLine get child2 => a2;
-
-  @override
-  bool get isMultiFrameMode => true;
-
-  @override
-  LvTestFeature buildFeature() => LvTestFeature(this);
-
-  @override
-  void onCancelingOnce() {}
-
-  static LvTest crateTest(ItemMeta meta, [IMeasureItem? parent]) {
-    var instance = LvTest(meta);
-    instance.calculator = _TestTwoDistanceSumCal(instance);
-
-    return instance;
-  }
-}
-
-class LvTestFeature extends TopMeasureItemFeature {
-  LvTestFeature(
-    ITopMeasureItem refItem,
-  ) : super(refItem);
-}
-
-class _TestTwoDistanceSumCal extends Calculator<LvTest, double> {
-  _TestTwoDistanceSumCal(LvTest ref) : super(ref);
-
-  @override
-  void calculate() {
-    if (ref.feature == null) return;
-
-    final f1 = findChildFeature(ref.child1);
-    final f2 = findChildFeature(ref.child2);
-    if (f1 == null || f2 == null) return;
-
-    final feature = ref.feature!;
-
-    final val1 = f1.value?.pickFloat() ?? 0;
-    final val2 = f2.value?.pickFloat() ?? 0;
-
-    final outputMeta = ref.meta.outputs.first;
-    double avg = (val1 + val2) / 2;
-    feature.updateFloatValue(outputMeta, avg, outputMeta.unit);
-  }
-}

+ 36 - 0
lib/process/primitives/combos/straightline_group.dart

@@ -0,0 +1,36 @@
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/items/item_metas.dart';
+import 'package:fis_measure/interfaces/process/items/types.dart';
+import 'package:fis_measure/process/calcuators/lv_study.dart';
+import 'package:fis_measure/process/items/top_item.dart';
+import 'package:fis_measure/process/items/top_item_feature.dart';
+import 'package:fis_measure/process/primitives/straightline.dart';
+
+class StraightLineGroup extends TopMeasureItem<StraightLineGroupFeature> {
+  StraightLineGroup(ItemMeta meta) : super(meta) {
+    for (final childMeta in meta.childItems) {
+      final childItem = StraightLine.createDistance(childMeta, this);
+      childItems.add(childItem);
+    }
+  }
+
+  @override
+  StraightLineGroupFeature buildFeature() => StraightLineGroupFeature(this);
+
+  static StraightLineGroup createLvStudy(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    if (meta.measureType != MeasureTypes.LvGroupByLineGroup) {
+      throw ArgumentError();
+    }
+    var ins = StraightLineGroup(meta);
+    ins.calculator = LvStudyDistanceGroupCal(ins);
+
+    return ins;
+  }
+}
+
+class StraightLineGroupFeature extends TopMeasureItemFeature {
+  StraightLineGroupFeature(
+    ITopMeasureItem refItem,
+  ) : super(refItem);
+}

+ 2 - 0
lib/process/unit/convert/convert.dart

@@ -5,6 +5,7 @@ import 'package:fis_measure/process/unit/convert/time.dart';
 import 'package:vid/us/vid_us_unit.dart';
 
 import 'abstract.dart';
+import 'volume.dart';
 
 /// 单位数值转换器
 class UnitValueConverter {
@@ -44,6 +45,7 @@ class UnitValueConverter {
     _add(TimeMap());
     _add(SpeedMap());
     _add(AreaMap());
+    _add(VolumeMap());
   }
 
   void _add(UnitMapBase item) {

+ 14 - 0
lib/process/unit/convert/volume.dart

@@ -0,0 +1,14 @@
+import 'package:vid/us/vid_us_unit.dart';
+
+import 'abstract.dart';
+
+class VolumeMap extends UnitMapBase {
+  VolumeMap() : super(VidUsUnit.cm3);
+
+  @override
+  void initMap() {
+    add(VidUsUnit.mm3, 0.01);
+    add(VidUsUnit.ml, 0.01);
+    add(VidUsUnit.L, 10000);
+  }
+}