Browse Source

update(measure): support Doppler Trace && Velocity

gavin.chen 2 năm trước cách đây
mục cha
commit
2bdb8bcea6

+ 26 - 0
assets/items.json

@@ -1,4 +1,30 @@
 [
+    {
+        "Name": "Velocity",
+        "Description": "Velocity",
+        "BriefAnnotation": "",
+        "MeasureTypeName": "Velocity",
+        "Categories": [
+            "Common"
+        ],
+        "Calculator": {
+            "AvailableOutputs": [
+                {
+                    "Name": "Velocity",
+                    "Description": "Velocity",
+                    "Unit": 70,
+                    "IsWorking": true
+                },
+                {
+                    "Name": "PG",
+                    "Description": "PG",
+                    "Unit": 110
+                }
+            ]
+        },
+        "MultiMethodItems": [],
+        "MethodChildItems": []
+    },
     {
         "Name": "TAMAX",
         "Description": "Mean Vel(Max Mode)",

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

@@ -623,4 +623,6 @@ class MeasureTerms {
   static const CervixL = "Cervix L";
   static const CervixW = "Cervix W";
   static const CervixH = "Cervix H";
+
+  static const Placeholder = "Placeholder";
 }

+ 1 - 0
lib/measure_page_test.dart

@@ -369,6 +369,7 @@ class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
     MeasureTerms.AbRatio,
     MeasureTerms.Slope,
     MeasureTerms.TAMAX,
+    MeasureTerms.Velocity,
     "Qp/Qs",
   ];
 

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

@@ -98,4 +98,103 @@ class GeneralFormulas {
     double slope = vertical / time;
     return slope;
   }
+
+  ///计算包络时间
+  ///Origin: Vinno.Modules.MeasureModule\Formulas\GeneralFormulas.cs [632:67]
+  static double countEnvelopeTime(List<DPoint> points) {
+    if (points.length < 2) return 0;
+    double startTime = double.maxFinite;
+    double endTime = -double.maxFinite;
+    for (var point in points) {
+      if (point.x < startTime) {
+        startTime = point.x;
+      }
+      if (point.x > endTime) {
+        endTime = point.x;
+      }
+    }
+    return (endTime - startTime).abs();
+  }
+
+  /// <summary>
+  /// 4.0 x V^2
+  /// </summary>
+  /// <param name="v">Unit cm/s</param>
+  /// <returns>Unit mmHg</returns>
+  static double countPressure(double v) {
+    // The velocity is in cm/s, but it should be m/s in this formula, so divide it by 100.
+    double pg = 4.0 * math.pow(v * 0.01, 2);
+    return pg;
+  }
+
+  // 计算心率
+  // TODO: 心脏周期(可配置) DefaultHeartCycle = 2;
+  // Origin: Vinno.Modules.MeasureModule\Primitives\DopplerTraceBase.cs
+  static double countHR(double timeSpan) {
+    const defaultHeartCycle = 1;
+    double hr = (defaultHeartCycle * 60 / timeSpan).roundToDouble();
+    return hr;
+  }
+
+  // return [VTI,tiEnv,timeAveragedVelocity,pv,meanPG,hr];
+  static List<double> countVTI(
+    List<DPoint> tracePoints,
+  ) {
+    var tiEnv = 0.0;
+    var timeAveragedVelocity = 0.0;
+    var pv = 0.0;
+    var meanPG = 0.0;
+    if (tracePoints.length < 2) return [0, 0, 0, 0, 0, 0];
+    int n = tracePoints.length - 1;
+    double dis = 0;
+    double startTime = 0;
+    double endTime = 0;
+    double pgSum = 0;
+    int positiveCount = 0;
+    int negativeCount = 0;
+    for (int i = 0; i < n; i++) {
+      var p1 = tracePoints[i];
+      var p2 = tracePoints[i + 1];
+      if (pv.abs() < (p1.y).abs()) {
+        pv = p1.y;
+      }
+      double meanVelocity = (p1.y + p2.y).abs() / 2;
+      double timeSpan = (p1.x - p2.x).abs();
+      dis += meanVelocity * timeSpan;
+      pgSum += countPressure(meanVelocity) * timeSpan;
+
+      if (i == 0) {
+        startTime = p1.x;
+        endTime = p2.x;
+      } else {
+        startTime = math.min(startTime, p2.x);
+        endTime = math.max(endTime, p2.x);
+      }
+
+      if (p1.y < 0) {
+        negativeCount++;
+      } else {
+        positiveCount++;
+      }
+    }
+    if (tracePoints[n].y < 0) {
+      negativeCount++;
+    } else {
+      positiveCount++;
+    }
+    if (positiveCount < negativeCount) {
+      dis = -dis;
+    }
+
+    if (pv.abs() < (tracePoints[n].y).abs()) {
+      pv = tracePoints[n].y;
+    }
+
+    tiEnv = (endTime - startTime).abs();
+    meanPG = (pgSum / tiEnv);
+    timeAveragedVelocity = (dis / tiEnv);
+    var hr = countHR(tiEnv);
+
+    return [dis, tiEnv, timeAveragedVelocity, pv, meanPG, hr];
+  }
 }

+ 134 - 7
lib/process/calcuators/trace.dart

@@ -1,5 +1,9 @@
+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/primitives/multi_method/multiple_trace.dart';
+import 'package:flutter/cupertino.dart';
 import 'calculator.dart';
 
 class TraceCal extends Calculator<TraceItemAbstract, double> {
@@ -11,16 +15,139 @@ class TraceCal extends Calculator<TraceItemAbstract, double> {
 
     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 points = feature.innerPoints.map((e) => viewport.convert(e)).toList();
-
+    // 翻转y轴坐标
+    final yFlippedPoints = regionPoints.map((e) => DPoint(e.x, -e.y)).toList();
+    // final points_X = points.map((e) => e.x).toList();
+    // print("横坐标集合: $points_X");
     double fakeOutputDate = 0;
+    var countVTIResult = GeneralFormulas.countVTI(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) {
-      if (output.name == MeasureTerms.Slope) {
-        feature.updateFloatValue(output, fakeOutputDate, output.unit);
-      } else if (output.name == MeasureTerms.Timespan) {
-        feature.updateFloatValue(output, fakeOutputDate, output.unit);
-      } else if (output.name == MeasureTerms.Distance) {
-        feature.updateFloatValue(output, fakeOutputDate, output.unit);
+      ///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:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.ED:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.MD:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.HeartRate:
+          feature.updateFloatValue(output, outputHR, output.unit);
+          break;
+        case MeasureTerms.Acceleration:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.AT:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.PSED:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.EDPS:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.PI:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.PIMD:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.RI:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.RIMD:
+          feature.updateFloatValue(output, fakeOutputDate, output.unit);
+          break;
+        case MeasureTerms.MaxPG:
+          feature.updateFloatValue(output, fakeOutputDate, 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, fakeOutputDate, 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:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.AVEL:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.EARatio:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.DT:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.PHT:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.VA:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.ADur:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.ATDTRatio:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.ATETRatio:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        // case MeasureTerms.Trace:
+        //   feature.updateFloatValue(output, fakeOutputDate, output.unit);
+        //   break;
+        default:
+          break;
       }
     }
   }

+ 20 - 0
lib/process/calcuators/velocity.dart

@@ -0,0 +1,20 @@
+import '../primitives/location.dart';
+import 'calculator.dart';
+
+class VelocityCal extends Calculator<Location, double> {
+  VelocityCal(Location ref) : super(ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+    final viewport = ref.feature!.hostVisualArea!.viewport!;
+    final layout = ref.feature!.hostVisualArea!.displayRegion;
+    final point = ref.feature!.point.clone();
+    point.addOffset(-layout.left, -layout.top);
+    // final physicalPoint = viewport.physical!.convert(point);
+    final physicalPoint = viewport.convert(point);
+    final coordinateOffset = viewport.region;
+    final value = -physicalPoint.y + coordinateOffset.top;
+    updateFloatValue(value);
+  }
+}

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

@@ -128,6 +128,7 @@ class MeasureItemFactory {
     _singleton._register(MeasureTypes.AbRatioTwoVerticalDistance,
         TwoStraightLine.createAbRatioTwoVerticalDistance);
     _singleton._register(MeasureTypes.Slope, StraightLine.createSlope);
+    _singleton._register(MeasureTypes.Velocity, Location.createVelocity);
     _singleton._register(MeasureTypes.DopplerTrace, MultiTrace.createTrace);
 
     // Area Perimeter

+ 10 - 0
lib/process/primitives/location.dart

@@ -9,6 +9,7 @@ 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/application.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/velocity.dart';
 import 'package:fis_measure/process/visual/tissue_area.dart';
 import 'package:fis_measure/utils/canvas.dart';
 import 'package:get/get.dart';
@@ -115,6 +116,15 @@ class Location extends MeasureItem<LocationFeature> {
     location.calculator = MDepthCal(location);
     return location;
   }
+
+  static Location createVelocity(
+    ItemMeta meta, [
+    IMeasureItem? parent,
+  ]) {
+    Location location = Location(meta, parent);
+    location.calculator = VelocityCal(location);
+    return location;
+  }
 }
 
 class TissueConvexLocationFeature extends LocationFeature {

+ 2 - 2
lib/process/primitives/multi_method/multiple_trace.dart

@@ -172,14 +172,14 @@ abstract class TraceItemFeatureAbstract extends MeasureItemFeature {
       handleChangeSide(point);
       if (point.x < innerPoints.last.x) {
         clearRight(point.x);
-      } else {
+      } else if (point.x > innerPoints.last.x) {
         innerPoints.add(point);
       }
     } else {
       handleChangeSide(point);
       if (point.x > innerPoints.last.x) {
         clearLeft(point.x);
-      } else {
+      } else if (point.x < innerPoints.last.x) {
         innerPoints.add(point);
       }
     }

+ 2 - 2
pubspec.lock

@@ -149,8 +149,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: "06b2cae"
-      resolved-ref: "06b2cae73b8bc11e399497d6b9d4491933a80d8c"
+      ref: c8c5122
+      resolved-ref: c8c5122aad7e44e9cefeb8a105aae37ca72e90cb
       url: "http://git.ius.plus:88/Project-Wing/fis_lib_jsonrpc.git"
     source: git
     version: "0.0.1"