Melon vor 11 Monaten
Ursprung
Commit
0c6d664fb5

+ 3 - 0
lib/configs/patient.dart

@@ -19,4 +19,7 @@ abstract class GlobalPatientConfig {
     }
     bsa = math.sqrt((height * weight) / 3600);
   }
+
+  /// 心率
+  static int? hr;
 }

+ 2 - 0
lib/interfaces/process/items/terms.dart

@@ -288,6 +288,8 @@ class MeasureTerms {
   /// 每搏量
   static const SV = "SV";
   static const SV_Card = "SV-Card";
+  static const SV_Diam = "SV Diam";
+  static const SV_Trace = "SV Trace";
 
   /// Ejection fraction
   /// 射血分数

+ 1 - 0
lib/interfaces/process/items/types.dart

@@ -141,6 +141,7 @@ class MeasureTypes {
   static const SemiManualTrace = "SemiManualTrace";
   static const DopplerTrace = "DopplerTrace";
   static const SlopeDoppler = "SlopeDoppler";
+  static const SV = "SV";
 
   /// 阻力指数
   static const ResistivityIndex = "ResistivityIndex";

+ 15 - 0
lib/process/calcuators/formulas/cardiac.dart

@@ -1,5 +1,6 @@
 import 'dart:math' as math;
 
+import 'package:fis_common/logger/logger.dart';
 import 'package:fis_measure/configs/cardiac.dart';
 import 'package:fis_measure/utils/number.dart';
 
@@ -161,4 +162,18 @@ class CardiacFormulas {
   static double mamPercent(double mapse, double lvidd, double lvids) {
     return mapse / (lvidd - lvids + mapse) * 100;
   }
+
+  /// SI
+  ///
+  /// (EDV - ESV)/BSA
+  ///
+  /// [sv] cm³
+  ///
+  /// [bsa] m²
+  ///
+  /// return `cm³/m²`
+  static double si(double sv, double bsa) {
+    double si = sv / bsa;
+    return si;
+  }
 }

+ 28 - 0
lib/process/calcuators/formulas/general.dart

@@ -5,6 +5,8 @@ import 'dart:math' as math;
 import 'package:fis_measure/interfaces/date_types/point.dart';
 
 class GeneralFormulas {
+  GeneralFormulas._();
+
   /// Volume:1/6 x π
   static const double VolumeCofficient = math.pi / 6.0;
 
@@ -227,4 +229,30 @@ class GeneralFormulas {
 
     return [dis, tiEnv, timeAveragedVelocity, pv, meanPG, hr];
   }
+
+  /// SV Diam
+  ///
+  /// SV = 1/4 x π x (Diam)^2 x VTI
+  ///
+  /// [diam] `cm`
+  ///
+  /// [vti] `cm`
+  ///
+  /// return `cm3`
+  static double svDiam(double diam, double vti) {
+    double sv = math.pi * math.pow(diam, 2) * vti / 4;
+    return sv;
+  }
+
+  /// CSA
+  ///
+  /// CSA = 1/4 x π x Diam^2
+  ///
+  /// [diam] `cm`
+  ///
+  /// return `cm²`
+  static double csa(double diam) {
+    double csa = math.pi * math.pow(diam, 2) / 4;
+    return csa;
+  }
 }

+ 1 - 1
lib/process/calcuators/lv_study.dart

@@ -301,5 +301,5 @@ class _ValTemp {
   double? lvedv;
   double? lvesv;
   double? sv;
-  int? hr = 72; // TODO: from vid ext
+  int? hr = GlobalPatientConfig.hr; // TODO: from vid ext
 }

+ 110 - 0
lib/process/calcuators/sv.dart

@@ -0,0 +1,110 @@
+import 'package:fis_measure/configs/patient.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/calcuators/formulas/cardiac.dart';
+import 'package:fis_measure/process/primitives/combos/sv.dart';
+import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+import 'calculator.dart';
+import 'formulas/general.dart';
+import 'trace.dart';
+
+class SvCal extends Calculator<Sv, double> {
+  SvCal(super.ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final feature = ref.feature!;
+
+    final diam = pickChildFloatValue(ref.l);
+    if (diam == null) {
+      updateStringValue("");
+      return;
+    }
+
+    double? vti;
+    double? sv;
+    int? hr;
+    double? co;
+    final bsa = GlobalPatientConfig.bsa;
+
+    TraceItemFeatureAbstract? traceFeature = ref.trace.feature;
+    if (traceFeature == null) {
+      if (ref.trace.measuredFeatures.isNotEmpty) {
+        traceFeature = ref.trace.measuredFeatures.first;
+      }
+    }
+
+    if (traceFeature != null) {
+      final countVTIResult = TraceCal.getCountVTI(traceFeature);
+      var outputVTI = countVTIResult[0];
+      var outputHR = countVTIResult[5];
+
+      vti = outputVTI;
+      hr = outputHR.toInt();
+
+      sv = GeneralFormulas.svDiam(diam, vti);
+    }
+
+    if (sv != null) {
+      updateFloatValue(sv, unit: VidUsUnit.cm3);
+    } else {
+      updateStringValue("");
+    }
+
+    // Diam 输出
+    // feature.updateFloatValue(ref.l.meta.outputs[0], diam, VidUsUnit.cm);
+
+    if (traceFeature != null) {
+      //  Trace 输出
+      for (var output in ref.trace.meta.outputs) {
+        if (output.name == MeasureTerms.SV_Trace) {
+          // feature.updateStringValue(output, '', VidUsUnit.None);
+        } else if (output.name == MeasureTerms.VTI) {
+          if (vti != null) {
+            feature.updateFloatValue(output, vti, VidUsUnit.cm);
+          }
+        } else if (output.name == MeasureTerms.HeartRate) {
+          if (hr != null) {
+            feature.updateFloatValue(output, hr.toDouble(), VidUsUnit.HR);
+          }
+        }
+      }
+    }
+
+    if (sv != null) {
+      for (var output in ref.meta.outputs) {
+        switch (output.name) {
+          case MeasureTerms.SI:
+            if (bsa > 0) {
+              final si = CardiacFormulas.si(sv, bsa);
+              feature.updateFloatValue(output, si, VidUsUnit.mlm2);
+            }
+            break;
+          case MeasureTerms.CO:
+            if (hr != null) {
+              co = CardiacFormulas.co(sv, hr: hr);
+              feature.updateFloatValue(output, co, VidUsUnit.Lmin);
+            }
+            break;
+          case MeasureTerms.CI:
+            if (co != null && bsa > 0) {
+              final ci = CardiacFormulas.ci(sv, hr: hr!, bsa: bsa);
+              feature.updateFloatValue(output, ci, VidUsUnit.Lminm2);
+            }
+            break;
+        }
+      }
+    }
+
+    final csaIndex =
+        ref.meta.outputs.indexWhere((e) => e.name == MeasureTerms.CSA);
+    if (csaIndex > -1) {
+      final output = ref.meta.outputs[csaIndex];
+      final csa = GeneralFormulas.csa(diam);
+      feature.updateFloatValue(output, csa, VidUsUnit.cm2);
+    }
+  }
+}

+ 23 - 0
lib/process/calcuators/trace.dart

@@ -2,6 +2,7 @@ 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/general.dart';
+import 'package:fis_measure/process/items/item_feature.dart';
 import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
 import 'calculator.dart';
 
@@ -146,4 +147,26 @@ class TraceCal extends Calculator<TraceItemAbstract, double> {
       }
     }
   }
+
+  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;
+  }
 }

+ 6 - 0
lib/process/items/factory.dart

@@ -9,6 +9,7 @@ import 'package:fis_measure/process/primitives/combos/depth2baseline.dart';
 import 'package:fis_measure/process/primitives/combos/lv_mass.dart';
 import 'package:fis_measure/process/primitives/combos/lwh_straightline.dart';
 import 'package:fis_measure/process/primitives/combos/lv_study.dart';
+import 'package:fis_measure/process/primitives/combos/sv.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';
@@ -92,6 +93,9 @@ class MeasureItemFactory {
 
     _singleton._register(MeasureTypes.LVdMass, LVMass.createLVdMass);
 
+    // SV
+    _singleton._register(MeasureTypes.SV, Sv.createSV);
+
     // Empty
     _singleton._register(MeasureTypes.Empty, Empty.createEmpty);
     // Locations
@@ -101,6 +105,8 @@ class MeasureItemFactory {
     // Two Locations
     _singleton._register(
         MeasureTypes.AbRatioTwoVelocity, TwoLocation.createAbRatioTwoVelocity);
+    _singleton._register(MeasureTypes.ResistivityIndex,
+        TwoLocation.createResistivityIndexTwoLocationByEd);
     _singleton._register(MeasureTypes.ResistivityIndexTwoLocationByEd,
         TwoLocation.createResistivityIndexTwoLocationByEd);
     _singleton._register(

+ 13 - 7
lib/process/items/item_feature.dart

@@ -123,17 +123,23 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
     double value,
     VidUsUnit unit,
   ) {
-    final valueBase =
-        values.firstWhereOrNull((e) => e.meta.name == outputMeta.name);
-    if (valueBase == null) {
+    int index = values.indexWhere((e) => e.meta.name == outputMeta.name);
+    if (index < 0) {
       final floatValue = FloatValue(outputMeta, value, unit);
       values.add(floatValue);
       return floatValue;
     } else {
-      final floatValue = valueBase as FloatValue;
-      floatValue.value = value;
-      floatValue.unit = unit;
-      return floatValue;
+      ValueBase valueBase = values[index];
+      if (valueBase is FloatValue) {
+        final floatValue = valueBase;
+        floatValue.value = value;
+        floatValue.unit = unit;
+        return floatValue;
+      } else {
+        final newValue = FloatValue(outputMeta, value, unit);
+        values[index] = newValue;
+        return newValue;
+      }
     }
   }
 

+ 37 - 0
lib/process/primitives/combos/sv.dart

@@ -0,0 +1,37 @@
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/items/item_metas.dart';
+import 'package:fis_measure/process/calcuators/sv.dart';
+import 'package:fis_measure/process/items/top_item.dart';
+import 'package:fis_measure/process/items/top_item_feature.dart';
+
+import '../multi_method/multiple_trace.dart';
+import '../straightline.dart';
+
+class Sv extends TopMeasureItem<SvFeature> {
+  late final StraightLine l;
+  late final MultiTrace trace;
+
+  Sv(super.meta) {
+    final metaDiam = meta.childItems[0];
+    final metaTA = meta.childItems[1];
+    l = StraightLine.createDistance(metaDiam, this);
+    trace = MultiTrace.createTrace(metaTA, this);
+    childItems.add(l);
+    childItems.add(trace);
+  }
+
+  @override
+  SvFeature buildFeature() => SvFeature(this);
+
+  static Sv createSV(ItemMeta meta, [IMeasureItem? parent]) {
+    var sv = Sv(meta);
+    sv.calculator = SvCal(sv);
+    return sv;
+  }
+}
+
+class SvFeature extends TopMeasureItemFeature {
+  SvFeature(
+    ITopMeasureItem refItem,
+  ) : super(refItem);
+}