Răsfoiți Sursa

1、测量库更新

guanxinyi 9 luni în urmă
părinte
comite
026dcc07c6

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

@@ -46,6 +46,9 @@ abstract class IMeasureItemFeature {
 
   /// 灌注图 绘制
   void paintPerfusion(Canvas canvas, Size size);
+
+  /// 设置URM 双幅图
+  void setURMDualImages(bool? isURMDualImages);
 }
 
 /// 测试项单次快照样式配置

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

@@ -160,6 +160,7 @@ class MeasureTerms {
   static const StdVessDiameter = "Std Diameter";
   static const MaxPos = "MaxPos";
   static const MinPos = "MinPos";
+  static const VesselCount = "VesselCount";
 
   static const URMPerfusionIndex = "Perfusion Index";
 

+ 41 - 0
lib/process/calcuators/urm_calcuators/urm_ab_ratio.dart

@@ -0,0 +1,41 @@
+import 'package:fis_measure/interfaces/process/calculators/values.dart';
+import 'package:fis_measure/process/calcuators/calculator.dart';
+import 'package:fis_measure/process/items/item.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_two_rect_den.dart';
+
+class AbRatioCal extends Calculator<TwoURMRectDen, double> {
+  AbRatioCal(TwoURMRectDen ref) : super(ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final a1 = _pickChildValue(ref.child1);
+    final a2 = _pickChildValue(ref.child2);
+
+    final feature = ref.feature!;
+    final viewport = feature.hostVisualArea!.viewport!;
+
+    // if (a1 != null && a2 != null) {
+    //   final value = GeneralFormulas.countStenosis(
+    //     a1,
+    //     a2,
+    //   );
+    //   updateFloatValue(value);
+    // }
+  }
+
+  double? _pickChildValue(MeasureItem item) {
+    if (item.calculator == null) return null;
+    ValueBase? value;
+    if (item.measuredFeatures.isNotEmpty) {
+      value = item.measuredFeatures.first.value;
+    } else if (item.feature != null) {
+      value = item.feature!.value;
+    }
+    if (value != null && value is FloatValue) {
+      return (value).value ?? 0;
+    }
+    return null;
+  }
+}

+ 34 - 7
lib/process/calcuators/urm_calcuators/urm_curve_curvature_line.dart

@@ -1,6 +1,8 @@
 import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/items/terms.dart';
 import 'package:fis_measure/process/primitives/trace.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_curvature_measure.dart';
 import 'package:fis_measure/process/workspace/urm/application.dart';
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:vid/us/vid_us_unit.dart';
@@ -33,16 +35,41 @@ class URMCurveCurvatureLineCal extends Calculator<Trace, double> {
         rOIType: URMROIType.URMTrace,
         srcDPoints: srcDPoints,
       );
-      if (result != null) {
-        print(
-            "URM Measure curvature: ${result.resultData} nums: ${result.resultDPoints?.length}");
+      if (result == null) return;
+      if (result.resultFlag) {
         final feature = ref.feature!;
-        for (var output in ref.meta.outputs) {
-          if (output.name == MeasureTerms.URMCurvature) {
-            output.unit = VidUsUnit.None;
-            feature.updateFloatValue(output, result.resultData, output.unit);
+        if (feature.refItem.parent is URMCurvatureMeasure) {
+          IMeasureItem? parent = feature.refItem.parent;
+          var revert =
+              parent?.meta.description == URMCurvatureMeasure.revertRatioName;
+          if (revert) {
+            for (var output in ref.meta.outputs) {
+              if (output.name == URMCurvatureMeasure.revertRatioName) {
+                output.unit = VidUsUnit.None;
+                feature.updateFloatValue(
+                    output, 1 / result.resultData, output.unit);
+              }
+            }
+          } else {
+            for (var output in ref.meta.outputs) {
+              if (output.name == URMCurvatureMeasure.ratioName) {
+                output.unit = VidUsUnit.None;
+                feature.updateFloatValue(
+                    output, result.resultData, output.unit);
+              }
+            }
+          }
+        } else {
+          /// TODO 后端配置 需要测试
+          for (var output in ref.meta.outputs) {
+            if (output.name == MeasureTerms.URMCurvature) {
+              output.unit = VidUsUnit.None;
+              feature.updateFloatValue(output, result.resultData, output.unit);
+            }
           }
         }
+        print(
+            "URM Measure curvature: ${result.resultData} nums: ${result.resultDPoints?.length}");
       } else {
         throw Exception("URM Measure API error");
       }

+ 6 - 4
lib/process/calcuators/urm_calcuators/urm_ellipse_density.dart

@@ -25,16 +25,18 @@ class URMEllipseDensityCal extends URMEllipseMeasureCal {
         rOIType: URMROIType.URMEllipse,
         srcDPoints: param!.srcDPoints,
       );
-      var result = outResult?.denMeasureResult;
-      var velResult = outResult?.velMeasureResult;
 
-      if (result != null && velResult != null) {
+      if (outResult != null) {
         final feature = ref.feature!;
 
         for (var output in ref.meta.outputs) {
           if (output.name == MeasureTerms.URMDenROI) {
             output.unit = VidUsUnit.percent;
-            feature.updateFloatValue(output, result.roiDen * 100, output.unit);
+            feature.updateFloatValue(
+              output,
+              outResult.resultData * 100,
+              output.unit,
+            );
           }
         }
       } else {

+ 3 - 30
lib/process/calcuators/urm_calcuators/urm_ellipse_measure.dart

@@ -17,35 +17,6 @@ class URMEllipseMeasureCal extends Calculator<URMEllipseMeasure, double> {
   @override
   void calculate() {}
 
-  @override
-  Future<void> calculateAsync() async {
-    if (ref.feature == null) return;
-    try {
-      if (ref.application is! URMApplication) {
-        return;
-      }
-
-      Size urmResultSize = const Size(0, 0);
-      final URMApplication urmApplication = ref.application as URMApplication;
-      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
-          urmApplication.resultHeight.toDouble());
-      final List<UrmPoint> points = ref.feature!.innerPoints
-          .map(
-            (e) => UrmPoint(
-              x: e.scale2Size(urmResultSize).x,
-              y: e.scale2Size(urmResultSize).y,
-            ),
-          )
-          .toList();
-
-      String description = "URM\n Calculating...";
-      updateStringValue(description);
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
   /// 处理获取到的URM参数
   void handleURMSimpleMeasureParams() {
     if (ref.feature == null) return;
@@ -61,10 +32,12 @@ class URMEllipseMeasureCal extends Calculator<URMEllipseMeasure, double> {
 
       // 添加入参
       List<UrmPoint> srcDPoints = [];
+      // if (ref.feature is! EllipseFeature) {
+      //   srcDPoints = [];
+      // }
       for (var point in ref.feature!.innerPoints) {
         srcDPoints.add(urmApplication.localToView(point));
       }
-
       param = URMSimpleMeasureParams(
         urmMeasureType: URMMeasureType.URMCurvature,
         rOIType: URMROIType.URMEllipse,

+ 1 - 1
lib/process/calcuators/urm_calcuators/urm_ellipse_perfusion.dart

@@ -27,7 +27,7 @@ class URMEllipsePerfusionCal extends URMEllipseMeasureCal {
       URMMeasureProcessResult? outResult =
           await urmApplication.getURMMeasureResult(
         urmMeasureType: URMMeasureType.URMPerfusion,
-        rOIType: URMROIType.URMRect,
+        rOIType: URMROIType.URMEllipse,
         srcDPoints: param!.srcDPoints,
       );
 

+ 5 - 5
lib/process/calcuators/urm_calcuators/urm_vel.dart

@@ -10,12 +10,12 @@ class URMVelCal extends Calculator<URMVelAbstract, double> {
   void calculate() {
     if (ref.feature == null) return;
 
-    final a1 = _pickChildValue(ref.child1);
-    final a2 = _pickChildValue(ref.child2);
-    final a3 = _pickChildValue(ref.child2);
+    // final a1 = _pickChildValue(ref.child1);
+    // final a2 = _pickChildValue(ref.child2);
+    // final a3 = _pickChildValue(ref.child2);
 
-    final feature = ref.feature!;
-    final viewport = feature.hostVisualArea!.viewport!;
+    // final feature = ref.feature!;
+    // final viewport = feature.hostVisualArea!.viewport!;
 
     // if (a1 != null && a2 != null) {
     //   final value = GeneralFormulas.countStenosis(

+ 142 - 0
lib/process/calcuators/urm_calcuators/urm_vessel_measure.dart

@@ -0,0 +1,142 @@
+import 'dart:math';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_vessel_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+// import 'package:fis_measure/view/measure/measure_images_bar.dart';
+import 'dart:math' as math;
+import '../calculator.dart';
+
+class URMVesselMeasureCal extends Calculator<URMVesselMeasure, double> {
+  URMVesselMeasureCal(
+    URMVesselMeasure ref,
+  ) : super(ref);
+  URMSimpleMeasureParams? param;
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      URMMeasureProcessResult? outresult =
+          await urmApplication.getURMMeasureResult(
+        urmMeasureType: URMMeasureType.URMVesselMeasure,
+        rOIType: URMROIType.placeHolder_0,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      URMVessMeasureResult? vessMeasureResult = outresult.vessMeasureResult;
+      if (vessMeasureResult == null) {
+        return;
+      }
+      if (ref.meta.outputs.isNotEmpty) {
+        final feature = ref.feature!;
+        if (feature == null) return;
+        final viewport = feature.hostVisualArea!.viewport!;
+        final p1 = feature.startPoint;
+        final p2 = feature.endPoint;
+        final pp1 = viewport.convert(p1);
+        final pp2 = viewport.convert(p2);
+        final cmlength = (pp2 - pp1).length.abs();
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.MaxVessDistance) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.maxVessDistance, output.unit);
+          } else if (output.name == MeasureTerms.MinVessDistance) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.minVessDistance, output.unit);
+          } else if (output.name == MeasureTerms.MeanVessDistacne) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.meanVessDistacne, output.unit);
+          } else if (output.name == MeasureTerms.StdVessDistance) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output,
+                math.sqrt(vessMeasureResult.varianceVessDistance), output.unit);
+          } else if (output.name == MeasureTerms.MaxVessDiameter) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.maxVessDiameter, output.unit);
+          } else if (output.name == MeasureTerms.MinVessDiameter) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.minVessDiameter, output.unit);
+          } else if (output.name == MeasureTerms.MeanVessDiameter) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.meanVessDiameter, output.unit);
+          } else if (output.name == MeasureTerms.StdVessDiameter) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output,
+                math.sqrt(vessMeasureResult.varianceVessDiameter), output.unit);
+          } else if (output.name == MeasureTerms.VesselCount) {
+            // output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, vessMeasureResult.vesselCount.toDouble(), output.unit);
+          } else if (output.name == MeasureTerms.Distance) {
+            // output.unit = VidUsUnit.None;
+
+            feature.updateFloatValue(output, cmlength, output.unit);
+          }
+          List<Point<num>> points = [];
+          outresult.resultDPoints?.forEach((element) {
+            points.add(Point(element.x, element.y));
+          });
+
+          urmApplication.onUpdateChart?.call(
+            URMChartParams(
+              cmlength: cmlength,
+              minPointIndex: vessMeasureResult.minPos,
+              maxPointIndex: vessMeasureResult.maxPos,
+              points: points,
+            ),
+          );
+        }
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+
+  /// 处理获取到的URM参数
+  void handleURMSimpleMeasureParams() {
+    if (ref.feature == null) return;
+
+    try {
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      final p1 = ref.feature!.startPoint;
+      final p2 = ref.feature!.endPoint;
+
+      param = URMSimpleMeasureParams(
+        urmMeasureType: URMMeasureType.URMVesselMeasure,
+        rOIType: URMROIType.placeHolder_0,
+        srcDPoints: [
+          urmApplication.localToView(p1),
+          urmApplication.localToView(p2)
+        ],
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 22 - 4
lib/process/items/factory.dart

@@ -19,9 +19,9 @@ import 'package:fis_measure/process/primitives/combos/two_location.dart';
 import 'package:fis_measure/process/primitives/combos/two_ray.dart';
 import 'package:fis_measure/process/primitives/combos/two_straightline.dart';
 import 'package:fis_measure/process/primitives/combos/two_sv.dart';
-import 'package:fis_measure/process/primitives/urm_measure/ab_ratio.dart';
 import 'package:fis_measure/process/primitives/urm_measure/urm_den.dart';
 import 'package:fis_measure/process/primitives/urm_measure/urm_trace_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_two_rect_den.dart';
 import 'package:fis_measure/process/primitives/urm_measure/urm_vel.dart';
 import 'package:fis_measure/process/primitives/detection.dart';
 import 'package:fis_measure/process/primitives/ellipse.dart';
@@ -255,7 +255,7 @@ class MeasureItemFactory {
     // URM 超分辨
 
     // _singleton._register(MeasureTypes.URMCurvature,
-    //     URMStraightCurvatureLineMeasure.createURMStraightCurvatureLineMeasure);
+    //     URMCurvatureMeasure.createURMCurvatureMeasure);
     _singleton._register(MeasureTypes.URMStraightCurvatureLineMeasure,
         URMStraightCurvatureLineMeasure.createURMStraightCurvatureLineMeasure);
     _singleton._register(MeasureTypes.URMCurveCurvatureLineMeasure,
@@ -288,8 +288,9 @@ class MeasureItemFactory {
     //     AutoUterusVertical.CreateAutoUterusVertical);
     // _singleton._register(
     //     MeasureTypes.AutoUterusCross, AutoUterusCross.CreateAutoUterusCross);
-    _singleton._register(MeasureTypes.URMDensityRation, AbRatio.create);
-    _singleton._register(MeasureTypes.URMFractalDimRation, AbRatio.create);
+
+    // _singleton._register(MeasureTypes.URMDensityRation, AbRatio.create);
+    // _singleton._register(MeasureTypes.URMFractalDimRation, AbRatio.create);
     _singleton._register(
         MeasureTypes.URMRectDenMeasure, URMRectMeasure.createURMRectDenMeasure);
     _singleton._register(MeasureTypes.URMTraceDenMeasure,
@@ -354,6 +355,23 @@ class MeasureItemFactory {
     _singleton._register(MeasureTypes.URMShellDenVelMeasure,
         URMShellMeasure.createURMShellDenVelMeasure);
 
+    /// 新增的
+    _singleton._register(
+        MeasureTypes.DensityTwoURMRect, TwoURMRectDen.createTwoURMRectDen);
+
+    _singleton._register(
+        MeasureTypes.DensityTwoTrace, TwoURMTraceDen.createTwoURMTraceDen);
+    _singleton._register(MeasureTypes.DensityTwoEllipse,
+        TwoURMEllipseDen.createTwoURMEllipseDen);
+
+    /// 遗漏项
+    // _singleton._register(MeasureTypes.FractalDimTwoURMRect,
+    //     TwoURMRectFractal.CreateTwoURMRectFractal);
+    // _singleton._register(MeasureTypes.FractalDimTwoURMTrace,
+    //     TwoURMTraceFractal.CreateTwoURMTraceFractal);
+    // _singleton._register(MeasureTypes.FractalDimTwoURMEllipse,
+    //     TwoURMEllipseFractal.CreateTwoURMEllipseFractal);
+
     _singleton._register(
       MeasureTypes.SemiautoTrace,
       SemiautoTrace.createTrace,

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

@@ -28,6 +28,8 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
   int _activeIndex = -1;
   Rect? zoomRect; // 当前缩放区域的归一化坐标
 
+  bool? isURMDualImages; // 是否是URM 双幅图
+
   @override
   int? frameIndex;
 
@@ -205,6 +207,11 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
     zoomRect = rect;
   }
 
+  @override
+  void setURMDualImages(bool? isURMDual) {
+    isURMDualImages = isURMDual;
+  }
+
   @override
   void paintPerfusion(
     Canvas canvas,

+ 36 - 16
lib/process/primitives/ellipse.dart

@@ -227,6 +227,7 @@ class EllipseFeature extends AreaItemFeatureAbstract {
         height: radiusY * 2,
       ),
     );
+
     canvas.drawPath(
       dashPath(
         path,
@@ -234,6 +235,7 @@ class EllipseFeature extends AreaItemFeatureAbstract {
       ),
       paintPan,
     );
+
     canvas.restore();
 
     if (activeIndex == 1) {
@@ -259,8 +261,10 @@ class EllipseFeature extends AreaItemFeatureAbstract {
     var p1 = xAxisStart;
     var p2 = xAxisEnd;
     if (fitSize != null) {
-      p1 = p1.scale2Size(fitSize);
-      p2 = p2.scale2Size(fitSize);
+      p1 = convert2ViewPoint(fitSize, p1);
+      p2 = convert2ViewPoint(fitSize, p2);
+      // p1 = p1.scale2Size(fitSize);
+      // p2 = p2.scale2Size(fitSize);
     }
     return (p2 - p1).length / 2;
   }
@@ -272,9 +276,12 @@ class EllipseFeature extends AreaItemFeatureAbstract {
       var p2 = yAxisEnd;
       var c = centroid;
       if (fitSize != null) {
-        p = p.scale2Size(fitSize);
-        p2 = p2.scale2Size(fitSize);
-        c = c.scale2Size(fitSize);
+        p = convert2ViewPoint(fitSize, p);
+        p2 = convert2ViewPoint(fitSize, p2);
+        c = convert2ViewPoint(fitSize, c);
+        // p = p.scale2Size(fitSize);
+        // p2 = p2.scale2Size(fitSize);
+        // c = c.scale2Size(fitSize);
       }
       return (p - p2).length / 2;
     } else {
@@ -301,27 +308,40 @@ class EllipseFeature extends AreaItemFeatureAbstract {
   /// 计算Y轴坐标点
   void adjustPoints(DPoint point) {
     if (activeIndex < 1) return;
-
-    final refSize = refItem.application.displaySize;
+    Size refSize;
+    if (refItem.feature?.isURMDualImages ?? false) {
+      refSize = Size(refItem.application.displaySize.width / 2,
+          refItem.application.displaySize.height);
+    } else {
+      refSize = refItem.application.displaySize;
+    }
+    // final refSize = Size(refItem.application.displaySize.width / 2,
+    //     refItem.application.displaySize.height);
     //  const refSize= Size(1000, 1000);
     // const restoreSize = Size(1 / 1000, 1 / 1000);
     final restoreSize = Size(1 / refSize.width, 1 / refSize.height);
-    final p = point.scale2Size(refSize);
-    final p1 = xAxisStart.scale2Size(refSize);
-    final p2 = xAxisEnd.scale2Size(refSize);
+    // final p = point.scale2Size(refSize);
+    // final p1 = xAxisStart.scale2Size(refSize);
+    // final p2 = xAxisEnd.scale2Size(refSize);
+
+    final p = convert2ViewPoint(refSize, point);
+    final p1 = convert2ViewPoint(refSize, xAxisStart);
+    final p2 = convert2ViewPoint(refSize, xAxisEnd);
     final double radius;
     if (activeIndex == 2) {
       radius = GeneralFormulas.distance2Line(p1, p2, p);
     } else {
       radius = getRadiusX(refSize);
     }
-    final crossPoints = _findCrossPoints(p2, p1, radius);
-    final p3Index = _findNearest(p, crossPoints);
-    innerPoints[2] = crossPoints[p3Index].scale2Size(restoreSize);
-    innerPoints[3] = crossPoints[1 - p3Index].scale2Size(restoreSize);
+    final crossPoints = findCrossPoints(p2, p1, radius);
+    final p3Index = findNearest(p, crossPoints);
+    // innerPoints[2] = crossPoints[p3Index].scale2Size(restoreSize);
+    // innerPoints[3] = crossPoints[1 - p3Index].scale2Size(restoreSize);
+    innerPoints[2] = convert2ViewPoint(restoreSize, crossPoints[p3Index]);
+    innerPoints[3] = convert2ViewPoint(restoreSize, crossPoints[1 - p3Index]);
   }
 
-  static List<DPoint> _findCrossPoints(DPoint a, DPoint b, distance) {
+  List<DPoint> findCrossPoints(DPoint a, DPoint b, distance) {
     final dx = b.x - a.x;
     final dy = b.y - a.y;
 
@@ -349,7 +369,7 @@ class EllipseFeature extends AreaItemFeatureAbstract {
     return [out1, out2];
   }
 
-  static int _findNearest(DPoint p, List<DPoint> source) {
+  int findNearest(DPoint p, List<DPoint> source) {
     int rst = 0;
     double minLen = (source[0] - p).length;
     for (var i = 1; i < source.length; i++) {

+ 26 - 7
lib/process/primitives/urm_measure/ab_ratio.dart

@@ -1,15 +1,34 @@
 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/distance.dart';
-import 'package:fis_measure/process/primitives/straightline.dart';
+import 'package:fis_measure/process/items/item.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';
 
 /// TODO 暂时这样写
-class AbRatio extends StraightLine {
-  AbRatio(super.meta, super.parent);
+class AbRatio {
+  AbRatio(meta, parent);
 
   static AbRatio create(ItemMeta meta, [IMeasureItem? parent]) {
-    AbRatio measureVessel = AbRatio(meta, parent);
-    measureVessel.calculator = DistanceCal(measureVessel);
-    return measureVessel;
+    var urmAbRatio = AbRatio(meta, parent);
+
+    return urmAbRatio;
   }
 }
+
+abstract class URMAbRatioAbstract<T extends MeasureItemFeature>
+    extends TopMeasureItem<T> {
+  URMAbRatioAbstract(ItemMeta meta) : super(meta);
+
+  MeasureItem get child1;
+  MeasureItem get child2;
+
+  @override
+  bool get finishAfterUnactive => true;
+}
+
+class URMAbRatioFeature extends TopMeasureItemFeature {
+  URMAbRatioFeature(
+    ITopMeasureItem refItem,
+  ) : super(refItem);
+}

+ 18 - 0
lib/process/primitives/urm_measure/urm_curvature_measure.dart

@@ -0,0 +1,18 @@
+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';
+
+class URMCurvatureMeasure {
+  static String revertRatioName = "URMCurvature(E/A)";
+  static String ratioName = "URMCurvature(A/E)";
+  URMCurvatureMeasure(ItemMeta meta, IMeasureItem parent);
+
+  static URMCurvatureMeasure createURMCurvatureMeasure(
+      ItemMeta meta, IMeasureItem parent) {
+    if (meta.measureType != MeasureTypes.URMCurvature) {
+      throw ArgumentError();
+    }
+    var item = URMCurvatureMeasure(meta, parent);
+    return item;
+  }
+}

+ 1 - 0
lib/process/primitives/urm_measure/urm_ellipse_measure.dart

@@ -187,6 +187,7 @@ class EllipsePerfusionImageFeature extends EllipseFeature {
       convert2ViewPoint(size, rightBottomPoint!);
       Rect dst = Rect.fromPoints(
           Offset(leftTop.x, leftTop.y), Offset(rightBottom.x, rightBottom.y));
+
       canvas.drawImageRect(perfusionImg!, src, dst, paint);
     }
   }

+ 139 - 0
lib/process/primitives/urm_measure/urm_two_rect_den.dart

@@ -0,0 +1,139 @@
+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/urm_calcuators/urm_ab_ratio.dart';
+import 'package:fis_measure/process/items/item.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/urm_measure/urm_rect_measure.dart';
+
+/// TODO 暂时这样写 -需要优化一下下
+///
+class TwoURMRectDen extends URMTwoURMRectDenAbstract<URMTwoURMRectDenFeature> {
+  static const String _area1Key = "Roi1";
+  static const String _area2Key = "Roi2";
+
+  late final URMRectMeasure a1;
+  late final URMRectMeasure a2;
+
+  TwoURMRectDen(ItemMeta meta) : super(meta) {
+    final metaA1 = meta.getChildByName(_area1Key)!;
+    final metaA2 = meta.getChildByName(_area2Key)!;
+    a1 = URMRectMeasure.createURMRectDensity(metaA1, this);
+    a2 = URMRectMeasure.createURMRectDensity(metaA2, this);
+    childItems.add(a1);
+    childItems.add(a2);
+  }
+
+  @override
+  URMRectMeasure get child1 => a1;
+
+  @override
+  URMRectMeasure get child2 => a2;
+
+  @override
+  URMTwoURMRectDenFeature buildFeature() => URMTwoURMRectDenFeature(this);
+
+  @override
+  void onCancelingOnce() {}
+
+  static TwoURMRectDen createTwoURMRectDen(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    var urmTwoURMRectDen = TwoURMRectDen(meta);
+    urmTwoURMRectDen.calculator = AbRatioCal(urmTwoURMRectDen);
+
+    return urmTwoURMRectDen;
+  }
+}
+
+class TwoURMTraceDen extends URMTwoURMRectDenAbstract<URMTwoURMRectDenFeature> {
+  static const String _area1Key = "Roi1";
+  static const String _area2Key = "Roi2";
+
+  late final URMRectMeasure a1;
+  late final URMRectMeasure a2;
+
+  TwoURMTraceDen(ItemMeta meta) : super(meta) {
+    final metaA1 = meta.getChildByName(_area1Key)!;
+    final metaA2 = meta.getChildByName(_area2Key)!;
+    a1 = URMRectMeasure.createURMRectDensity(metaA1, this);
+    a2 = URMRectMeasure.createURMRectDensity(metaA2, this);
+    childItems.add(a1);
+    childItems.add(a2);
+  }
+
+  @override
+  URMRectMeasure get child1 => a1;
+
+  @override
+  URMRectMeasure get child2 => a2;
+
+  @override
+  URMTwoURMRectDenFeature buildFeature() => URMTwoURMRectDenFeature(this);
+
+  @override
+  void onCancelingOnce() {}
+
+  static TwoURMRectDen createTwoURMTraceDen(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    var urmTwoURMRectDen = TwoURMRectDen(meta);
+    urmTwoURMRectDen.calculator = AbRatioCal(urmTwoURMRectDen);
+
+    return urmTwoURMRectDen;
+  }
+}
+
+class TwoURMEllipseDen
+    extends URMTwoURMRectDenAbstract<URMTwoURMRectDenFeature> {
+  static const String _area1Key = "Roi1";
+  static const String _area2Key = "Roi2";
+
+  late final URMRectMeasure a1;
+  late final URMRectMeasure a2;
+
+  TwoURMEllipseDen(ItemMeta meta) : super(meta) {
+    final metaA1 = meta.getChildByName(_area1Key)!;
+    final metaA2 = meta.getChildByName(_area2Key)!;
+    a1 = URMRectMeasure.createURMRectDensity(metaA1, this);
+    a2 = URMRectMeasure.createURMRectDensity(metaA2, this);
+    childItems.add(a1);
+    childItems.add(a2);
+  }
+
+  @override
+  URMRectMeasure get child1 => a1;
+
+  @override
+  URMRectMeasure get child2 => a2;
+
+  @override
+  URMTwoURMRectDenFeature buildFeature() => URMTwoURMRectDenFeature(this);
+
+  @override
+  void onCancelingOnce() {}
+
+  static TwoURMRectDen createTwoURMEllipseDen(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    var urmTwoURMRectDen = TwoURMRectDen(meta);
+    urmTwoURMRectDen.calculator = AbRatioCal(urmTwoURMRectDen);
+
+    return urmTwoURMRectDen;
+  }
+}
+
+abstract class URMTwoURMRectDenAbstract<T extends MeasureItemFeature>
+    extends TopMeasureItem<T> {
+  URMTwoURMRectDenAbstract(ItemMeta meta) : super(meta);
+
+  MeasureItem get child1;
+  MeasureItem get child2;
+
+  @override
+  bool get finishAfterUnactive => true;
+}
+
+class URMTwoURMRectDenFeature extends TopMeasureItemFeature {
+  URMTwoURMRectDenFeature(
+    ITopMeasureItem refItem,
+  ) : super(refItem);
+}

+ 5 - 6
lib/process/primitives/urm_measure/urm_vel.dart

@@ -1,20 +1,19 @@
 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/urm.dart';
 import 'package:fis_measure/process/calcuators/urm_calcuators/urm_vel.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/urm_location.dart';
 import 'package:fis_measure/process/items/item.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/urm_measure/urm_location_vel.dart';
 
 class URMVel extends URMVelAbstract<URMVelFeature> {
-  static const String _area1Key = "SRLoactionVel";
+  static const String _area1Key = "URMLocationVel";
   static const String _area2Key = "Vessel diameter";
   static const String _area3Key = "Vessel distance";
 
-  late final URMLoaction a1;
+  late final URMLocationVelMeasure a1;
   late final StraightLine a2;
   late final StraightLine a3;
 
@@ -22,7 +21,7 @@ class URMVel extends URMVelAbstract<URMVelFeature> {
     final metaA1 = meta.getChildByName(_area1Key)!;
     final metaA2 = meta.getChildByName(_area2Key)!;
     final metaA3 = meta.getChildByName(_area3Key)!;
-    a1 = URMLoaction.createURMLocation(metaA1, this);
+    a1 = URMLocationVelMeasure.createURMLocationVelMeasure(metaA1, this);
     a2 = StraightLine.createDistance(metaA2, this);
     a3 = StraightLine.createDistance(metaA3, this);
     childItems.add(a1);
@@ -31,7 +30,7 @@ class URMVel extends URMVelAbstract<URMVelFeature> {
   }
 
   @override
-  URMLoaction get child1 => a1;
+  URMLocationVelMeasure get child1 => a1;
 
   @override
   StraightLine get child2 => a2;

+ 61 - 2
lib/process/primitives/urm_measure/urm_vessel_measure.dart

@@ -1,7 +1,11 @@
+import 'package:fis_measure/interfaces/enums/items.dart';
 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/distance.dart';
+import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_vessel_measure.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+import 'package:fis_measure/view/gesture/cross_position_indicator.dart';
 
 class URMVesselMeasure extends StraightLine {
   URMVesselMeasure(super.meta, super.parent);
@@ -9,7 +13,62 @@ class URMVesselMeasure extends StraightLine {
   static URMVesselMeasure createURMVesselMeasure(ItemMeta meta,
       [IMeasureItem? parent]) {
     URMVesselMeasure measureVessel = URMVesselMeasure(meta, parent);
-    measureVessel.calculator = DistanceCal(measureVessel);
+    measureVessel.calculator = URMVesselMeasureCal(measureVessel);
+
     return measureVessel;
   }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      } else {
+        return false;
+      }
+    }
+
+    if (state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleMouseDownWhileWaiting(args);
+      }
+    } else if (state == ItemStates.running) {
+      if (args.pointType == PointInfoType.mouseUp) return false;
+
+      feature?.endPoint = args;
+      doCalculate();
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleFinish();
+
+        ///重置十字样式
+        changeCrossIndicatorStyle(CrossIndicatorStyle.nomal);
+      }
+    }
+    return true;
+  }
+
+  @override
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    // TODO: 判断是否当前area
+    // 转换为Area逻辑位置
+    final point = args.toAreaLogicPoint();
+    // feature = StraightLineFeature(this, point, point);
+    if (args.hostVisualArea != null) {
+      handleTissueTM(args.hostVisualArea!.mode.modeType, point);
+      feature!.hostVisualArea = args.hostVisualArea;
+    }
+    state = ItemStates.running;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
 }