Эх сурвалжийг харах

Merge branch 'master' of http://git.ius.plus/Project-Wing/fis_lib_measure

Melon 9 сар өмнө
parent
commit
74b46f8496
66 өөрчлөгдсөн 4130 нэмэгдсэн , 1477 устгасан
  1. 3 0
      lib/interfaces/process/items/item_feature.dart
  2. 26 0
      lib/interfaces/process/items/terms.dart
  3. 71 29
      lib/interfaces/process/items/types.dart
  4. 6 0
      lib/interfaces/process/urm/urm_data_processor.dart
  5. 683 727
      lib/process/calcuators/urm.dart
  6. 59 0
      lib/process/calcuators/urm_calcuators/urm_curvature_trace_measure.dart
  7. 55 0
      lib/process/calcuators/urm_calcuators/urm_curve_curvature_line.dart
  8. 42 0
      lib/process/calcuators/urm_calcuators/urm_den.dart
  9. 68 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_den_measure.dart
  10. 82 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_den_vel_measure.dart
  11. 49 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_density.dart
  12. 49 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_fractal_dim.dart
  13. 24 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_hist.dart
  14. 77 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_measure.dart
  15. 68 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_perfusion.dart
  16. 64 0
      lib/process/calcuators/urm_calcuators/urm_ellipse_vel_measure.dart
  17. 66 0
      lib/process/calcuators/urm_calcuators/urm_location_vel.dart
  18. 0 216
      lib/process/calcuators/urm_calcuators/urm_rect.dart
  19. 68 0
      lib/process/calcuators/urm_calcuators/urm_rect_den_measure.dart
  20. 82 0
      lib/process/calcuators/urm_calcuators/urm_rect_den_vel_measure.dart
  21. 49 0
      lib/process/calcuators/urm_calcuators/urm_rect_density.dart
  22. 52 0
      lib/process/calcuators/urm_calcuators/urm_rect_fractal_dim.dart
  23. 28 0
      lib/process/calcuators/urm_calcuators/urm_rect_hist.dart
  24. 65 0
      lib/process/calcuators/urm_calcuators/urm_rect_measure.dart
  25. 69 0
      lib/process/calcuators/urm_calcuators/urm_rect_perfusion.dart
  26. 65 0
      lib/process/calcuators/urm_calcuators/urm_rect_vel_measure.dart
  27. 69 0
      lib/process/calcuators/urm_calcuators/urm_shell_den_measure.dart
  28. 88 0
      lib/process/calcuators/urm_calcuators/urm_shell_den_vel_measure.dart
  29. 44 0
      lib/process/calcuators/urm_calcuators/urm_shell_density_measure.dart
  30. 26 0
      lib/process/calcuators/urm_calcuators/urm_shell_fractal_dim_measure.dart
  31. 26 0
      lib/process/calcuators/urm_calcuators/urm_shell_hist_measure.dart
  32. 70 0
      lib/process/calcuators/urm_calcuators/urm_shell_measure.dart
  33. 26 0
      lib/process/calcuators/urm_calcuators/urm_shell_perfusion_measure.dart
  34. 26 0
      lib/process/calcuators/urm_calcuators/urm_shell_vel_measure.dart
  35. 69 0
      lib/process/calcuators/urm_calcuators/urm_straight_curvature_line.dart
  36. 71 0
      lib/process/calcuators/urm_calcuators/urm_trace_den_measure.dart
  37. 86 0
      lib/process/calcuators/urm_calcuators/urm_trace_den_vel_measure.dart
  38. 42 0
      lib/process/calcuators/urm_calcuators/urm_trace_density.dart
  39. 42 0
      lib/process/calcuators/urm_calcuators/urm_trace_fractal_dim.dart
  40. 45 0
      lib/process/calcuators/urm_calcuators/urm_trace_hist.dart
  41. 70 0
      lib/process/calcuators/urm_calcuators/urm_trace_measure.dart
  42. 66 0
      lib/process/calcuators/urm_calcuators/urm_trace_perfusion.dart
  43. 63 0
      lib/process/calcuators/urm_calcuators/urm_trace_vel_measure.dart
  44. 42 0
      lib/process/calcuators/urm_calcuators/urm_vel.dart
  45. 111 64
      lib/process/items/factory.dart
  46. 19 0
      lib/process/items/item_feature.dart
  47. 0 18
      lib/process/primitives/combos/urm_den_combo.dart
  48. 8 8
      lib/process/primitives/combos/urm_sr_roi_rect.dart
  49. 0 16
      lib/process/primitives/combos/urm_vel_combo.dart
  50. 1 1
      lib/process/primitives/urm_location.dart
  51. 15 0
      lib/process/primitives/urm_measure/ab_ratio.dart
  52. 96 0
      lib/process/primitives/urm_measure/urm_curvature_trace_measure.dart
  53. 60 0
      lib/process/primitives/urm_measure/urm_curve_curvature_line_measure.dart
  54. 35 17
      lib/process/primitives/urm_measure/urm_den.dart
  55. 186 0
      lib/process/primitives/urm_measure/urm_ellipse_measure.dart
  56. 19 0
      lib/process/primitives/urm_measure/urm_location_vel.dart
  57. 235 0
      lib/process/primitives/urm_measure/urm_rect_measure.dart
  58. 77 0
      lib/process/primitives/urm_measure/urm_shell_measure.dart
  59. 14 24
      lib/process/primitives/urm_measure/urm_straight_curvature_line_measure.dart
  60. 212 0
      lib/process/primitives/urm_measure/urm_trace_measure.dart
  61. 17 2
      lib/process/primitives/urm_measure/urm_vel.dart
  62. 15 0
      lib/process/primitives/urm_measure/urm_vessel_measure.dart
  63. 0 120
      lib/process/primitives/urm_rect.dart
  64. 1 1
      lib/process/primitives/urm_trace.dart
  65. 67 233
      lib/process/workspace/urm/application.dart
  66. 1 1
      pubspec.yaml

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

@@ -40,6 +40,9 @@ abstract class IMeasureItemFeature {
 
   /// 检查是否可绘制
   bool checkCanPaint();
+
+  /// 设置当前 zoom area 的归一化 Rect
+  void setZoomRect(Rect? zoomRect);
 }
 
 /// 测试项单次快照样式配置

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

@@ -120,6 +120,32 @@ class MeasureTerms {
   static const MaxPos = "MaxPos";
   static const MinPos = "MinPos";
 
+  static const URMPerfusionIndex = "Perfusion Index";
+
+  static const OutURMDenROI = "Vessel ratio(out)";
+  static const OutURMDenFractalDim = "Complexity level(out)";
+  static const OutURMDenMax = "MaxDen(out)";
+  static const OutURMDenMin = "MinDen(out)";
+  static const OutURMDenMean = "MeanDen(out)";
+  static const OutURMDenStd = "StdDen(out)";
+  static const OutURMVelMax = "MaxVel(out)";
+  static const OutURMVelMin = "MinVel(out)";
+  static const OutURMVelMean = "MeanVel(out)";
+  static const OutURMVelStd = "StdVel(out)";
+  static const OutArea = "Area(out)";
+
+  static const InURMDenROI = "Vessel ratio(in)";
+  static const InURMDenFractalDim = "Complexity level(in)";
+  static const InURMDenMax = "MaxDen(in)";
+  static const InURMDenMin = "MinDen(in)";
+  static const InURMDenMean = "MeanDen(in)";
+  static const InURMDenStd = "StdDen(in)";
+  static const InURMVelMax = "MaxVel(in)";
+  static const InURMVelMin = "MinVel(in)";
+  static const InURMVelMean = "MeanVel(in)";
+  static const InURMVelStd = "StdVel(in)";
+  static const InArea = "Area(in)";
+
   ///////////////////////////////////////////////////////////////////////////////////////////////////////
 
   /* AI [begin]*/

+ 71 - 29
lib/interfaces/process/items/types.dart

@@ -188,42 +188,84 @@ class MeasureTypes {
   /// 右室收缩压
   static const RVSP = "RVSP";
 
-  /* URM [begin]*/
-  static const SRStraightCurvatureLineMeasure =
-      "SRStraightCurvatureLineMeasure";
-  static const SRCurvature = "SRCurvature";
-  static const SRRoiDensity = "SRRoiDensity";
-  // static const SRRoiFractalDim = "SRROIFractalDim";
-  static const SRTraceDensity = "SRTraceDensity";
-  static const SRTraceFD = "SRTraceFD";
-  static const SRLoactionVel = "SRLoactionVel";
-  static const SRRoiVel = "SRRoiVel";
+  /* URM Measure [begin]*/
+  static const DensityTwoURMRect = "DensityTwoURMRect";
+
+  static const DensityTwoTrace = "DensityTwoTrace";
+
+  static const DensityTwoEllipse = "DensityTwoEllipse";
+
+  static const FractalDimTwoURMRect = "FractalDimTwoURMRect";
+
+  static const FractalDimTwoURMTrace = "FractalDimTwoURMTrace";
+
+  static const FractalDimTwoURMEllipse = "FractalDimTwoURMEllipse";
+
+  static const FractalDimURM = "FractalDimURM";
+
+  static const DensityURM = "DensityURM";
+
   static const URMDensityMeasure = "URMDensityMeasure";
-  static const URMRectDenMeasure = "URMRectDenMeasure";
+
   static const URMVelMeasure = "URMVelMeasure";
 
-  static const VesselDiameter = "VesselDiameter";
-  static const VesselDistance = "VesselDistance";
+  static const URMDensityVelMeasure = "URMDensityVelMeasure";
+
+  static const URMPerfusionMeasure = "URMPerfusionMeasure";
+
+  static const URMHistMeasure = "URMHistMeasure";
 
-  static const SRCurveCurvatureLineMeasure = "SRCurveCurvatureLineMeasure";
-  static const URMVesselMeasure = "URMVesselMeasure";
-  static const URMRectVelMeasure = "URMRectVelMeasure";
-  static const URMTraceDenMeasure = "URMTraceDenMeasure";
-  static const URMTraceVelMeasure = "URMTraceVelMeasure";
   static const URMShellDenMeasure = "URMShellDenMeasure";
-  static const URMShellFractalDim = "URMShellFractalDim";
-  static const URMShellDensity = "URMShellDensity";
   static const URMShellVelMeasure = "URMShellVelMeasure";
-  static const SRROIFractalDim = "SRROIFractalDim";
-  static const SRTraceFractalDim = "SRTraceFractalDim";
+  static const URMShellDenVelMeasure = "URMShellDenVelMeasure";
+  static const URMShellDensityMeasure = "URMShellDensityMeasure";
+  static const URMShellFractalDimMeasure = "URMShellFractalDimMeasure";
+  static const URMShellPerfusionMeasure = "URMShellPerfusionMeasure";
+  static const URMShellHistMeasure = "URMShellHistMeasure";
+
+  static const URMDensityRation = "URMDensityRation";
+
+  static const URMFractalDimRation = "URMFractalDimRation";
+
+  static const URMCurvature = "URMCurvature";
+
+  static const URMRectDensity = "URMRectDensity";
+
+  static const URMTraceDensity = "URMTraceDensity";
+
+  static const URMEllipseDensity = "URMEllipseDensity";
+
+  static const URMLocationVel = "URMLocationVel";
+
+  static const URMRectFractalDim = "URMRectFractalDim";
+
+  static const URMTraceFractalDim = "URMTraceFractalDim";
+
+  static const URMEllipseFractalDim = "URMEllipseFractalDim";
+
   static const URMDen = "URMDen";
-  static const URMVel = "URMVel";
-  static const FractalDimSR = "FractalDimSR";
-  static const DensitySR = "DensitySR";
 
-  static const DensityTwoSRRoiRect = "DensityTwoSRRoiRect";
-  static const FractalDimTwoSRRoiRect = "FractalDimTwoSRRoiRect";
+  static const URMVel = "URMVel";
 
-  static const Roi1 = "Roi1";
-  static const Roi2 = "Roi2";
+  static const URMRectDenMeasure = "URMRectDenMeasure";
+  static const URMRectVelMeasure = "URMRectVelMeasure";
+  static const URMRectDenVelMeasure = "URMRectDenVelMeasure";
+  static const URMTraceDenMeasure = "URMTraceDenMeasure";
+  static const URMTraceVelMeasure = "URMTraceVelMeasure";
+  static const URMTraceDenVelMeasure = "URMTraceDenVelMeasure";
+  static const URMEllipseDenMeasure = "URMEllipseDenMeasure";
+  static const URMEllipseVelMeasure = "URMEllipseVelMeasure";
+  static const URMEllipseDenVelMeasure = "URMEllipseDenVelMeasure";
+  static const URMVesselMeasure = "URMVesselMeasure";
+  static const URMRectPerfusion = "URMRectPerfusion";
+  static const URMTracePerfusion = "URMTracePerfusion";
+  static const URMEllipsePerfusion = "URMEllipsePerfusion";
+  static const URMRectHist = "URMRectHist";
+  static const URMTraceHist = "URMTraceHist";
+  static const URMEllipseHist = "URMEllipseHist";
+
+  static const URMStraightCurvatureLineMeasure =
+      "URMStraightCurvatureLineMeasure";
+  static const URMCurveCurvatureLineMeasure = "URMCurveCurvatureLineMeasure";
+  static const URMCurvatureTraceMeasure = "URMCurvatureTraceMeasure";
 }

+ 6 - 0
lib/interfaces/process/urm/urm_data_processor.dart

@@ -1,7 +1,13 @@
+import 'dart:ui';
+
 import 'package:fis_jsonrpc/services/aIDiagnosis.m.dart';
 
 abstract class IURMDataProcessor {
   URMMeasureParams getURMMeasureParams();
+  Rect get measureAreaInFullVisual; // 当前可测量区域在完整视图中的归一化矩形
+  Rect get urmScreenInFullVisual; // 当前URM视图在完整视图中的归一化矩形
+  double get phywidth; // 图像物理宽度
+  bool get showLeftRight; // 是否显示左右双幅
 }
 
 class URMMeasureParams {

+ 683 - 727
lib/process/calcuators/urm.dart

@@ -1,727 +1,683 @@
-import 'dart:math';
-import 'dart:ui';
-
-import 'package:fis_common/logger/logger.dart';
-import 'package:fis_jsonrpc/rpc.dart';
-import 'package:fis_measure/interfaces/date_types/point.dart';
-import 'package:fis_measure/interfaces/process/calculators/values.dart';
-import 'package:fis_measure/interfaces/process/items/terms.dart';
-import 'package:fis_measure/interfaces/process/items/types.dart';
-import 'package:fis_measure/process/items/item.dart';
-import 'package:fis_measure/process/primitives/area_abstract.dart';
-import 'package:fis_measure/process/primitives/combos/urm_den_combo.dart';
-import 'package:fis_measure/process/primitives/combos/urm_sr_roi_rect_combo.dart';
-import 'package:fis_measure/process/primitives/combos/urm_vel_combo.dart';
-import 'package:fis_measure/process/primitives/location.dart';
-import 'package:fis_measure/process/primitives/straightline.dart';
-import 'package:fis_measure/process/primitives/urm_rect.dart';
-import 'package:fis_measure/process/primitives/urm_straightline.dart';
-import 'package:fis_measure/process/workspace/urm/application.dart';
-import 'package:vid/us/vid_us_unit.dart';
-
-import 'calculator.dart';
-
-class URMRectCal extends Calculator<URMRect, double> {
-  URMRectCal(
-    URMRect ref, {
-    required this.type,
-  }) : super(ref);
-  final String type;
-  @override
-  void calculate() {}
-
-  @override
-  Future<void> calculateAsync() async {
-    if (ref.feature == null) return;
-    Size urmResultSize = const Size(0, 0);
-    try {
-      if (ref.application is! URMApplication) {
-        return;
-      }
-      final URMApplication urmApplication = ref.application as URMApplication;
-      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
-          urmApplication.resultHeight.toDouble());
-      final p1 = ref.feature!.startPoint;
-      final p2 = ref.feature!.endPoint;
-      //左上顶点
-      final leftTopPercent =
-          DPoint(p1.x < p2.x ? p1.x : p2.x, p1.y < p2.y ? p1.y : p2.y);
-      //右下顶点
-      final rightBottomPercent =
-          DPoint(p1.x > p2.x ? p1.x : p2.x, p1.y > p2.y ? p1.y : p2.y);
-
-      final startPoint = leftTopPercent.scale2Size(urmResultSize);
-      final endPoint = rightBottomPercent.scale2Size(urmResultSize);
-
-      String description = "URM\n Calculating...";
-      updateStringValue(description);
-
-      // switch (type) {
-      //   case MeasureTypes.DensitySR:
-      //   case MeasureTypes.SRRoiDensity:
-      //   case MeasureTypes.Roi1:
-      //   case MeasureTypes.Roi2:
-      //     await getSRRoiDensityResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.FractalDimSR:
-      //   case MeasureTypes.SRROIFractalDim:
-      //     await getSRRoiFractalDimResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.SRRoiVel:
-      //     await getSRRoiVelResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.URMVelMeasure:
-      //     await getURMVelMeasureResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   default:
-      // }
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
-  // //✅ URM 测量项 2
-  // Future<void> getSRRoiDensityResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiVelResult? result =
-  //         await app.getSRRoiVelResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if ([MeasureTerms.URMDenROI, MeasureTerms.URMDenFractalDim]
-  //             .contains(output.name)) {
-  //           output.unit = VidUsUnit.percent;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 3 未验证需要组合测量项
-  // Future<void> getSRRoiFractalDimResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiFractalDimResult? result =
-  //         await app.getSRRoiFractalDimResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMDenFractalDim) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 7 未验证需要组合测量项
-  // Future<void> getSRRoiVelResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiVelResult? result =
-  //         await app.getSRRoiVelResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.SRRoiVel) {
-  //           output.unit = VidUsUnit.percent;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 9
-  // Future<void> getURMVelMeasureResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetURMVelMeasureResult? result =
-  //         await app.getURMVelMeasureResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       // TODO 全部内容展示
-  //       print("URM Measure inMaxVel: ${result.inMaxVel} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMVelMax) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.maxVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelMin) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.minVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelMean) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.meanVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelStd) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, sqrt(result.varianceVel), VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.Area) {
-  //           output.unit = VidUsUnit.cm2;
-  //           feature.updateFloatValue(output, result.roiArea, VidUsUnit.cm2);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-}
-
-class URMLocationCal extends Calculator<Location, double> {
-  URMLocationCal(Location ref) : super(ref);
-
-  @override
-  void calculate() {}
-
-  @override
-  Future<void> calculateAsync() async {
-    if (ref.feature == null) return;
-    Size urmResultSize = const Size(0, 0);
-    try {
-      if (ref.application is! URMApplication) {
-        return;
-      }
-      final URMApplication urmApplication = ref.application as URMApplication;
-      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
-          urmApplication.resultHeight.toDouble());
-      // urmResultWidth = urmApplication.resultWidth;
-      // urmResultHeight = urmApplication.resultHeight;
-      final point = ref.feature!.point.clone();
-      final startPoint = point.scale2Size(urmResultSize);
-      print("URM Measure startPoint: $startPoint");
-      // await getSRLoactionVelResult(urmApplication, startPoint);
-      return;
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
-  // ✅ URM 测量项 6
-  // Future<void> getSRLoactionVelResult(
-  //     URMApplication app, DPoint startPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRLoactionVelResult? result =
-  //         await app.getSRLoactionVelResult(startPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output}");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.SRVel) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-}
-
-/// 自动弯曲度测量
-class URMAutoLineLineCal extends Calculator<StraightLine, double> {
-  URMAutoLineLineCal(
-    StraightLine ref, {
-    required this.type,
-  }) : super(ref);
-  final String type;
-  @override
-  void calculate() {}
-
-  @override
-  Future<void> calculateAsync() async {
-    try {
-      if (ref.feature == null) return;
-      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 p1 = ref.feature!.startPoint;
-      final p2 = ref.feature!.endPoint;
-
-      // final startPoint = leftTopPercent.scale2Size(urmResultSize);
-      // final endPoint = rightBottomPercent.scale2Size(urmResultSize);
-      // final startPoint = DPoint(0.478335, 0.315384);
-      // final endPoint = DPoint(0.538395, 0.526373);
-
-      switch (type) {
-        case MeasureTypes.SRCurvature:
-          await getSRCurvatureResult(urmApplication, p1, p2);
-          break;
-        // case MeasureTypes.URMDensityMeasure:
-        //   await getURMDenMeasureResult(urmApplication, startPoint, endPoint);
-        //   break;
-        // case MeasureTypes.URMVesselMeasure:
-        //   final feature = ref.feature!;
-        //   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();
-        //   await getURMVessMeasureResult(
-        //       urmApplication, startPoint, endPoint, cmlength);
-        //   break;
-        default:
-      }
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
-  // // ✅ URM 测量项 1
-  Future<void> getSRCurvatureResult(
-      URMApplication app, DPoint startPoint, DPoint endPoint) async {
-    String description = "URM\n Measuring";
-    try {
-      URMMeasureProcessResult? result = await app.getURMMeasureResult(
-        urmMeasureType: URMMeasureType.URMCurvature,
-        rOIType: URMROIType.placeHolder_0,
-        srcDPoints: [
-          UrmPoint(x: startPoint.x, y: startPoint.y),
-          UrmPoint(x: endPoint.x, y: endPoint.y)
-        ],
-      );
-      if (result != null) {
-        print(
-            "URM Measure curvature: ${result} nums: ${result.resultDPoints?.length}");
-        // TODO 将返回的视图百分比区域的点集转换为全图百分比点集,然会然后绘制
-        final feature = ref.feature!;
-        if (feature is! URMAutoLineFeature) return;
-        feature.autoLinePoints = urmPointsToDPoints(result.resultDPoints);
-
-        /// TODO 只转数据类型还不够,需要将 视图区域百分比坐标转为全图百分比坐标
-        for (var output in ref.meta.outputs) {
-          if (output.name == MeasureTerms.SRCurvature) {
-            output.unit = VidUsUnit.None;
-            feature.updateFloatValue(output, result.resultData, output.unit);
-          }
-        }
-      } else {
-        throw Exception("URM Measure API error");
-      }
-    } catch (e) {
-      description = " ";
-      updateStringValue(description);
-      return;
-    }
-    ref.application.updateRenderReady.emit(this, null);
-  }
-
-  // //✅ URM 测量项 8
-  // Future<void> getURMDenMeasureResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetURMDenMeasureResult? result =
-  //         await app.getURMDenMeasureResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print(
-  //           "URM Measure inMaxDensity: ${result.inMaxDensity} inMeanDensity: ${result.inMeanDensity}");
-  //       // description = "${(result.inMaxDensity).toStringAsFixed(2)}mm/s ...";
-  //       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, VidUsUnit.percent);
-  //         }
-  //         if (output.name == MeasureTerms.URMDenFractalDim) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(
-  //               output, result.roiFractalDim, VidUsUnit.None);
-  //         }
-  //         if (output.name == MeasureTerms.URMDenMax) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(output, result.maxDensity, VidUsUnit.None);
-  //         }
-  //         if (output.name == MeasureTerms.URMDenMin) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(output, result.minDensity, VidUsUnit.None);
-  //         }
-  //         if (output.name == MeasureTerms.URMDenMean) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(
-  //               output, result.meanDensity, VidUsUnit.None);
-  //         }
-  //         if (output.name == MeasureTerms.URMDenStd) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(
-  //               output, sqrt(result.varianceDensity), VidUsUnit.None);
-  //         }
-  //         if (output.name == MeasureTerms.Area) {
-  //           output.unit = VidUsUnit.cm2;
-  //           feature.updateFloatValue(output, result.roiArea, VidUsUnit.cm2);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 10
-  // Future<void> getURMVessMeasureResult(URMApplication app, DPoint startPoint,
-  //     DPoint endPoint, double cmlength) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetUrmVessMeasureResult? result =
-  //         await app.getURMVessMeasureResult(startPoint, endPoint, cmlength);
-  //     if (result != null) {
-  //       app.onUpdateChart?.call(
-  //         URMChartParams(
-  //           cmlength: cmlength,
-  //           minPointIndex: result.minPos,
-  //           maxPointIndex: result.maxPos,
-  //           points: convertPoints(result.outputPoints),
-  //         ),
-  //       );
-  //       print(
-  //           "URM Measure inMaxDensity: ${result.maxVessDiameter} inMeanDensity: ${result.meanVessDistacne}");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.MaxVessDistance) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.maxVessDistance, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.MinVessDistance) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.minVessDistance, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.MeanVessDistacne) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.meanVessDistacne, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.StdVessDistance) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, sqrt(result.varianceVessDistance), VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.MaxVessDiameter) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.maxVessDiameter, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.MinVessDiameter) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.minVessDiameter, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.MeanVessDiameter) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, result.meanVessDiameter, VidUsUnit.mm);
-  //         }
-  //         if (output.name == MeasureTerms.StdVessDiameter) {
-  //           output.unit = VidUsUnit.mm;
-  //           feature.updateFloatValue(
-  //               output, sqrt(result.varianceVessDiameter), VidUsUnit.mm);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  List<DPoint> urmPointsToDPoints(List<UrmPoint>? points) {
-    if (points == null) return [];
-    List<DPoint> urmPoints = [];
-    for (var point in points) {
-      urmPoints.add(DPoint(point.x, point.y));
-    }
-    return urmPoints;
-  }
-
-  //
-}
-
-class URMTraceCal extends Calculator<AreaItemAbstract, double> {
-  URMTraceCal(
-    AreaItemAbstract ref, {
-    required this.type,
-  }) : super(ref);
-  final String type;
-
-  @override
-  void calculate() {}
-
-  @override
-  Future<void> calculateAsync() async {
-    if (ref.feature == null) return;
-    final feature = ref.feature!;
-    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<DPoint> points =
-          feature.innerPoints.map((e) => e.scale2Size(urmResultSize)).toList();
-
-      // switch (type) {
-      //   case MeasureTypes.SRTraceDensity:
-      //     await getSRTraceVelResult(urmApplication, convertPoints(points));
-      //     break;
-      //   case MeasureTypes.SRTraceFractalDim:
-      //     await getSRTraceFractalDimResult(
-      //         urmApplication, convertPoints(points));
-      //     break;
-      //   default:
-      // }
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
-  // //✅ URM 测量项 4
-  // Future<void> getSRTraceVelResult(
-  //     URMApplication app, List<UrmPoint> points) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRTraceVelResult? result = await app.getSRTraceVelResult(points);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMDenROI) {
-  //           output.unit = VidUsUnit.percent;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 5
-  // Future<void> getSRTraceFractalDimResult(
-  //     URMApplication app, List<UrmPoint> points) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRTraceFractalDimResult? result =
-  //         await app.getSRTraceFractalDimResult(points);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMDenFractalDim) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  List<UrmPoint> convertPoints(List<DPoint> points) {
-    List<UrmPoint> urmPoints = [];
-    for (var point in points) {
-      urmPoints.add(UrmPoint(x: point.x, y: point.y));
-    }
-    return urmPoints;
-  }
-}
-
-class URMVelCal extends Calculator<URMVelAbstract, double> {
-  URMVelCal(URMVelAbstract ref) : super(ref);
-
-  @override
-  void calculate() {
-    if (ref.feature == null) return;
-
-    final a1 = _pickChildValue(ref.child1);
-    final a2 = _pickChildValue(ref.child2);
-    final a3 = _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;
-  }
-}
-
-class URMDenCal extends Calculator<URMDenAbstract, double> {
-  URMDenCal(URMDenAbstract ref) : super(ref);
-
-  @override
-  void calculate() {
-    if (ref.feature == null) return;
-
-    final a1 = _pickChildValue(ref.child1);
-    final a2 = _pickChildValue(ref.child2);
-    final a3 = _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;
-  }
-}
-
-class URMSrDensityCal extends Calculator<URMTwoSRRoiRectAbstract, double> {
-  URMSrDensityCal(URMTwoSRRoiRectAbstract ref) : super(ref);
-
-  @override
-  void calculate() {
-    if (ref.feature == null) return;
-
-    final a1 = _pickChildValue(ref.child1);
-    final a2 = _pickChildValue(ref.child2);
-    final a3 = _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;
-  }
-}
+// import 'dart:math';
+// import 'dart:ui';
+
+// import 'package:fis_common/logger/logger.dart';
+// import 'package:fis_jsonrpc/rpc.dart';
+// import 'package:fis_measure/interfaces/date_types/point.dart';
+// import 'package:fis_measure/interfaces/process/calculators/values.dart';
+// import 'package:fis_measure/process/items/item.dart';
+// import 'package:fis_measure/process/primitives/area_abstract.dart';
+// import 'package:fis_measure/process/primitives/combos/urm_sr_roi_rect_combo.dart';
+// import 'package:fis_measure/process/primitives/location.dart';
+// import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+// // import 'package:fis_measure/process/primitives/urm_straightline.dart';
+// import 'package:fis_measure/process/workspace/urm/application.dart';
+
+// import 'calculator.dart';
+
+// class URMRectCal extends Calculator<URMRectMeasure, double> {
+//   URMRectCal(
+//     URMRectMeasure ref, {
+//     required this.type,
+//   }) : super(ref);
+//   final String type;
+//   @override
+//   void calculate() {}
+
+//   @override
+//   Future<void> calculateAsync() async {
+//     if (ref.feature == null) return;
+//     Size urmResultSize = const Size(0, 0);
+//     try {
+//       if (ref.application is! URMApplication) {
+//         return;
+//       }
+//       final URMApplication urmApplication = ref.application as URMApplication;
+//       urmResultSize = Size(urmApplication.resultWidth.toDouble(),
+//           urmApplication.resultHeight.toDouble());
+//       final p1 = ref.feature!.startPoint;
+//       final p2 = ref.feature!.endPoint;
+//       //左上顶点
+//       final leftTopPercent =
+//           DPoint(p1.x < p2.x ? p1.x : p2.x, p1.y < p2.y ? p1.y : p2.y);
+//       //右下顶点
+//       final rightBottomPercent =
+//           DPoint(p1.x > p2.x ? p1.x : p2.x, p1.y > p2.y ? p1.y : p2.y);
+
+//       final startPoint = leftTopPercent.scale2Size(urmResultSize);
+//       final endPoint = rightBottomPercent.scale2Size(urmResultSize);
+
+//       String description = "URM\n Calculating...";
+//       updateStringValue(description);
+
+//       // switch (type) {
+//       //   case MeasureTypes.DensitySR:
+//       //   case MeasureTypes.SRRoiDensity:
+//       //   case MeasureTypes.Roi1:
+//       //   case MeasureTypes.Roi2:
+//       //     await getSRRoiDensityResult(urmApplication, startPoint, endPoint);
+//       //     break;
+//       //   case MeasureTypes.FractalDimSR:
+//       //   case MeasureTypes.SRROIFractalDim:
+//       //     await getSRRoiFractalDimResult(urmApplication, startPoint, endPoint);
+//       //     break;
+//       //   case MeasureTypes.SRRoiVel:
+//       //     await getSRRoiVelResult(urmApplication, startPoint, endPoint);
+//       //     break;
+//       //   case MeasureTypes.URMVelMeasure:
+//       //     await getURMVelMeasureResult(urmApplication, startPoint, endPoint);
+//       //     break;
+//       //   default:
+//       // }
+//     } catch (e) {
+//       logger.e('URM Measure error: $e');
+//       return;
+//     }
+//   }
+
+//   // //✅ URM 测量项 2
+//   // Future<void> getSRRoiDensityResult(
+//   //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRRoiVelResult? result =
+//   //         await app.getSRRoiVelResult(startPoint, endPoint);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if ([MeasureTerms.URMDenROI, MeasureTerms.URMDenFractalDim]
+//   //             .contains(output.name)) {
+//   //           output.unit = VidUsUnit.percent;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+
+//   // //✅ URM 测量项 3 未验证需要组合测量项
+//   // Future<void> getSRRoiFractalDimResult(
+//   //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRRoiFractalDimResult? result =
+//   //         await app.getSRRoiFractalDimResult(startPoint, endPoint);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.URMDenFractalDim) {
+//   //           output.unit = VidUsUnit.None;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+
+//   // //✅ URM 测量项 7 未验证需要组合测量项
+//   // Future<void> getSRRoiVelResult(
+//   //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRRoiVelResult? result =
+//   //         await app.getSRRoiVelResult(startPoint, endPoint);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.SRRoiVel) {
+//   //           output.unit = VidUsUnit.percent;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+
+//   // //✅ URM 测量项 9
+//   // Future<void> getURMVelMeasureResult(
+//   //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetURMVelMeasureResult? result =
+//   //         await app.getURMVelMeasureResult(startPoint, endPoint);
+//   //     if (result != null) {
+//   //       // TODO 全部内容展示
+//   //       print("URM Measure inMaxVel: ${result.inMaxVel} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.URMVelMax) {
+//   //           output.unit = VidUsUnit.mms;
+//   //           feature.updateFloatValue(
+//   //               output, result.maxVel + app.urmMinVel, VidUsUnit.mms);
+//   //         }
+//   //         if (output.name == MeasureTerms.URMVelMin) {
+//   //           output.unit = VidUsUnit.mms;
+//   //           feature.updateFloatValue(
+//   //               output, result.minVel + app.urmMinVel, VidUsUnit.mms);
+//   //         }
+//   //         if (output.name == MeasureTerms.URMVelMean) {
+//   //           output.unit = VidUsUnit.mms;
+//   //           feature.updateFloatValue(
+//   //               output, result.meanVel + app.urmMinVel, VidUsUnit.mms);
+//   //         }
+//   //         if (output.name == MeasureTerms.URMVelStd) {
+//   //           output.unit = VidUsUnit.mms;
+//   //           feature.updateFloatValue(
+//   //               output, sqrt(result.varianceVel), VidUsUnit.mms);
+//   //         }
+//   //         if (output.name == MeasureTerms.Area) {
+//   //           output.unit = VidUsUnit.cm2;
+//   //           feature.updateFloatValue(output, result.roiArea, VidUsUnit.cm2);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+// }
+
+// class URMLocationCal extends Calculator<Location, double> {
+//   URMLocationCal(Location ref) : super(ref);
+
+//   @override
+//   void calculate() {}
+
+//   @override
+//   Future<void> calculateAsync() async {
+//     if (ref.feature == null) return;
+//     Size urmResultSize = const Size(0, 0);
+//     try {
+//       if (ref.application is! URMApplication) {
+//         return;
+//       }
+//       final URMApplication urmApplication = ref.application as URMApplication;
+//       urmResultSize = Size(urmApplication.resultWidth.toDouble(),
+//           urmApplication.resultHeight.toDouble());
+//       // urmResultWidth = urmApplication.resultWidth;
+//       // urmResultHeight = urmApplication.resultHeight;
+//       final point = ref.feature!.point.clone();
+//       final startPoint = point.scale2Size(urmResultSize);
+//       print("URM Measure startPoint: $startPoint");
+//       // await getSRLoactionVelResult(urmApplication, startPoint);
+//       return;
+//     } catch (e) {
+//       logger.e('URM Measure error: $e');
+//       return;
+//     }
+//   }
+
+//   // ✅ URM 测量项 6
+//   // Future<void> getSRLoactionVelResult(
+//   //     URMApplication app, DPoint startPoint) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRLoactionVelResult? result =
+//   //         await app.getSRLoactionVelResult(startPoint);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output}");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.SRVel) {
+//   //           output.unit = VidUsUnit.mms;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+// }
+
+// /// 自动弯曲度测量
+// // class URMAutoLineLineCal extends Calculator<StraightLine, double> {
+// //   URMAutoLineLineCal(
+// //     StraightLine ref, {
+// //     required this.type,
+// //   }) : super(ref);
+// //   final String type;
+// //   @override
+// //   void calculate() {}
+
+// //   @override
+// //   Future<void> calculateAsync() async {
+// //     try {
+// //       if (ref.feature == null) return;
+// //       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 p1 = ref.feature!.startPoint;
+// //       final p2 = ref.feature!.endPoint;
+
+// //       // final startPoint = leftTopPercent.scale2Size(urmResultSize);
+// //       // final endPoint = rightBottomPercent.scale2Size(urmResultSize);
+// //       // final startPoint = DPoint(0.478335, 0.315384);
+// //       // final endPoint = DPoint(0.538395, 0.526373);
+
+// //       switch (type) {
+// //         case MeasureTypes.SRCurvature:
+// //           await getSRCurvatureResult(urmApplication, p1, p2);
+// //           break;
+// //         // case MeasureTypes.URMDensityMeasure:
+// //         //   await getURMDenMeasureResult(urmApplication, startPoint, endPoint);
+// //         //   break;
+// //         // case MeasureTypes.URMVesselMeasure:
+// //         //   final feature = ref.feature!;
+// //         //   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();
+// //         //   await getURMVessMeasureResult(
+// //         //       urmApplication, startPoint, endPoint, cmlength);
+// //         //   break;
+// //         default:
+// //       }
+// //     } catch (e) {
+// //       logger.e('URM Measure error: $e');
+// //       return;
+// //     }
+// //   }
+
+// //   // // ✅ URM 测量项 1
+// //   Future<void> getSRCurvatureResult(
+// //       URMApplication app, DPoint startPoint, DPoint endPoint) async {
+// //     String description = "URM\n Measuring";
+// //     try {
+// //       URMMeasureProcessResult? result = await app.getURMMeasureResult(
+// //         urmMeasureType: URMMeasureType.URMCurvature,
+// //         rOIType: URMROIType.placeHolder_0,
+// //         srcDPoints: [
+// //           UrmPoint(x: startPoint.x, y: startPoint.y),
+// //           UrmPoint(x: endPoint.x, y: endPoint.y)
+// //         ],
+// //       );
+// //       if (result != null) {
+// //         print(
+// //             "URM Measure curvature: ${result} nums: ${result.resultDPoints?.length}");
+// //         // TODO 将返回的视图百分比区域的点集转换为全图百分比点集,然会然后绘制
+// //         final feature = ref.feature!;
+// //         if (feature is! StraightCurvatureFeature) return;
+// //         feature.autoLinePoints = urmPointsToDPoints(result.resultDPoints);
+
+// //         /// TODO 只转数据类型还不够,需要将 视图区域百分比坐标转为全图百分比坐标
+// //         for (var output in ref.meta.outputs) {
+// //           if (output.name == MeasureTerms.SRCurvature) {
+// //             output.unit = VidUsUnit.None;
+// //             feature.updateFloatValue(output, result.resultData, output.unit);
+// //           }
+// //         }
+// //       } else {
+// //         throw Exception("URM Measure API error");
+// //       }
+// //     } catch (e) {
+// //       description = " ";
+// //       updateStringValue(description);
+// //       return;
+// //     }
+// //     ref.application.updateRenderReady.emit(this, null);
+// //   }
+
+// //   // //✅ URM 测量项 8
+// //   // Future<void> getURMDenMeasureResult(
+// //   //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
+// //   //   String description = "URM\n Measuring";
+// //   //   try {
+// //   //     GetURMDenMeasureResult? result =
+// //   //         await app.getURMDenMeasureResult(startPoint, endPoint);
+// //   //     if (result != null) {
+// //   //       print(
+// //   //           "URM Measure inMaxDensity: ${result.inMaxDensity} inMeanDensity: ${result.inMeanDensity}");
+// //   //       // description = "${(result.inMaxDensity).toStringAsFixed(2)}mm/s ...";
+// //   //       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, VidUsUnit.percent);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.URMDenFractalDim) {
+// //   //           output.unit = VidUsUnit.None;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.roiFractalDim, VidUsUnit.None);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.URMDenMax) {
+// //   //           output.unit = VidUsUnit.None;
+// //   //           feature.updateFloatValue(output, result.maxDensity, VidUsUnit.None);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.URMDenMin) {
+// //   //           output.unit = VidUsUnit.None;
+// //   //           feature.updateFloatValue(output, result.minDensity, VidUsUnit.None);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.URMDenMean) {
+// //   //           output.unit = VidUsUnit.None;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.meanDensity, VidUsUnit.None);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.URMDenStd) {
+// //   //           output.unit = VidUsUnit.None;
+// //   //           feature.updateFloatValue(
+// //   //               output, sqrt(result.varianceDensity), VidUsUnit.None);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.Area) {
+// //   //           output.unit = VidUsUnit.cm2;
+// //   //           feature.updateFloatValue(output, result.roiArea, VidUsUnit.cm2);
+// //   //         }
+// //   //       }
+// //   //     } else {
+// //   //       throw Exception("URM Measure API error");
+// //   //     }
+// //   //   } catch (e) {
+// //   //     description = " ";
+// //   //     updateStringValue(description);
+// //   //     return;
+// //   //   }
+// //   //   ref.application.updateRenderReady.emit(this, null);
+// //   // }
+
+// //   // //✅ URM 测量项 10
+// //   // Future<void> getURMVessMeasureResult(URMApplication app, DPoint startPoint,
+// //   //     DPoint endPoint, double cmlength) async {
+// //   //   String description = "URM\n Measuring";
+// //   //   try {
+// //   //     GetUrmVessMeasureResult? result =
+// //   //         await app.getURMVessMeasureResult(startPoint, endPoint, cmlength);
+// //   //     if (result != null) {
+// //   //       app.onUpdateChart?.call(
+// //   //         URMChartParams(
+// //   //           cmlength: cmlength,
+// //   //           minPointIndex: result.minPos,
+// //   //           maxPointIndex: result.maxPos,
+// //   //           points: convertPoints(result.outputPoints),
+// //   //         ),
+// //   //       );
+// //   //       print(
+// //   //           "URM Measure inMaxDensity: ${result.maxVessDiameter} inMeanDensity: ${result.meanVessDistacne}");
+// //   //       final feature = ref.feature!;
+// //   //       for (var output in ref.meta.outputs) {
+// //   //         if (output.name == MeasureTerms.MaxVessDistance) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.maxVessDistance, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.MinVessDistance) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.minVessDistance, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.MeanVessDistacne) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.meanVessDistacne, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.StdVessDistance) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, sqrt(result.varianceVessDistance), VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.MaxVessDiameter) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.maxVessDiameter, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.MinVessDiameter) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.minVessDiameter, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.MeanVessDiameter) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, result.meanVessDiameter, VidUsUnit.mm);
+// //   //         }
+// //   //         if (output.name == MeasureTerms.StdVessDiameter) {
+// //   //           output.unit = VidUsUnit.mm;
+// //   //           feature.updateFloatValue(
+// //   //               output, sqrt(result.varianceVessDiameter), VidUsUnit.mm);
+// //   //         }
+// //   //       }
+// //   //     } else {
+// //   //       throw Exception("URM Measure API error");
+// //   //     }
+// //   //   } catch (e) {
+// //   //     description = " ";
+// //   //     updateStringValue(description);
+// //   //     return;
+// //   //   }
+// //   //   ref.application.updateRenderReady.emit(this, null);
+// //   // }
+
+// //   List<DPoint> urmPointsToDPoints(List<UrmPoint>? points) {
+// //     if (points == null) return [];
+// //     List<DPoint> urmPoints = [];
+// //     for (var point in points) {
+// //       urmPoints.add(DPoint(point.x, point.y));
+// //     }
+// //     return urmPoints;
+// //   }
+
+// //   //
+// // }
+
+// class URMTraceCal extends Calculator<AreaItemAbstract, double> {
+//   URMTraceCal(
+//     AreaItemAbstract ref, {
+//     required this.type,
+//   }) : super(ref);
+//   final String type;
+
+//   @override
+//   void calculate() {}
+
+//   @override
+//   Future<void> calculateAsync() async {
+//     if (ref.feature == null) return;
+//     final feature = ref.feature!;
+//     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<DPoint> points =
+//           feature.innerPoints.map((e) => e.scale2Size(urmResultSize)).toList();
+
+//       // switch (type) {
+//       //   case MeasureTypes.SRTraceDensity:
+//       //     await getSRTraceVelResult(urmApplication, convertPoints(points));
+//       //     break;
+//       //   case MeasureTypes.SRTraceFractalDim:
+//       //     await getSRTraceFractalDimResult(
+//       //         urmApplication, convertPoints(points));
+//       //     break;
+//       //   default:
+//       // }
+//     } catch (e) {
+//       logger.e('URM Measure error: $e');
+//       return;
+//     }
+//   }
+
+//   // //✅ URM 测量项 4
+//   // Future<void> getSRTraceVelResult(
+//   //     URMApplication app, List<UrmPoint> points) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRTraceVelResult? result = await app.getSRTraceVelResult(points);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.URMDenROI) {
+//   //           output.unit = VidUsUnit.percent;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+
+//   // //✅ URM 测量项 5
+//   // Future<void> getSRTraceFractalDimResult(
+//   //     URMApplication app, List<UrmPoint> points) async {
+//   //   String description = "URM\n Measuring";
+//   //   try {
+//   //     GetSRTraceFractalDimResult? result =
+//   //         await app.getSRTraceFractalDimResult(points);
+//   //     if (result != null) {
+//   //       print("URM Measure output: ${result.output} ");
+//   //       final feature = ref.feature!;
+//   //       for (var output in ref.meta.outputs) {
+//   //         if (output.name == MeasureTerms.URMDenFractalDim) {
+//   //           output.unit = VidUsUnit.None;
+//   //           feature.updateFloatValue(output, result.output, output.unit);
+//   //         }
+//   //       }
+//   //     } else {
+//   //       throw Exception("URM Measure API error");
+//   //     }
+//   //   } catch (e) {
+//   //     description = " ";
+//   //     updateStringValue(description);
+
+//   //     return;
+//   //   }
+//   //   ref.application.updateRenderReady.emit(this, null);
+//   // }
+
+//   List<UrmPoint> convertPoints(List<DPoint> points) {
+//     List<UrmPoint> urmPoints = [];
+//     for (var point in points) {
+//       urmPoints.add(UrmPoint(x: point.x, y: point.y));
+//     }
+//     return urmPoints;
+//   }
+// }
+
+// // class URMVelCal extends Calculator<URMVelAbstract, double> {
+// //   URMVelCal(URMVelAbstract ref) : super(ref);
+
+// //   @override
+// //   void calculate() {
+// //     if (ref.feature == null) return;
+
+// //     final a1 = _pickChildValue(ref.child1);
+// //     final a2 = _pickChildValue(ref.child2);
+// //     final a3 = _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;
+// //   }
+// // }
+
+// class URMSrDensityCal extends Calculator<URMTwoSRRoiRectAbstract, double> {
+//   URMSrDensityCal(URMTwoSRRoiRectAbstract ref) : super(ref);
+
+//   @override
+//   void calculate() {
+//     if (ref.feature == null) return;
+
+//     final a1 = _pickChildValue(ref.child1);
+//     final a2 = _pickChildValue(ref.child2);
+//     final a3 = _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;
+//   }
+// }

+ 59 - 0
lib/process/calcuators/urm_calcuators/urm_curvature_trace_measure.dart

@@ -0,0 +1,59 @@
+import 'package:fis_common/logger/logger.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_trace_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';
+import '../calculator.dart';
+
+/// 曲线弯曲度计算
+class URMCurvatureTraceMeasureCal extends Calculator<Trace, double> {
+  URMCurvatureTraceMeasureCal(
+    Trace ref,
+  ) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      // 添加入参
+      List<UrmPoint> srcDPoints = [];
+      for (var point in ref.feature!.innerPoints) {
+        srcDPoints.add(urmApplication.localToView(point));
+      }
+
+      URMMeasureProcessResult? result =
+          await urmApplication.getURMMeasureResult(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: srcDPoints,
+      );
+      if (result != null) {
+        print(
+            "URM Measure curvature: ${result.resultData} nums: ${result.resultDPoints?.length}");
+        final feature = ref.feature!;
+        if (feature is! TraceCurvatureFeature) return;
+        feature.autoLinePoints =
+            urmApplication.urmPointsToDPoints(result.resultDPoints);
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.SRCurvature) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.resultData, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 55 - 0
lib/process/calcuators/urm_calcuators/urm_curve_curvature_line.dart

@@ -0,0 +1,55 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/primitives/trace.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import '../calculator.dart';
+
+/// 曲线弯曲度计算
+class URMCurveCurvatureLineCal extends Calculator<Trace, double> {
+  URMCurveCurvatureLineCal(
+    Trace ref,
+  ) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      // 添加入参
+      List<UrmPoint> srcDPoints = [];
+      for (var point in ref.feature!.innerPoints) {
+        srcDPoints.add(urmApplication.localToView(point));
+      }
+
+      URMMeasureProcessResult? result =
+          await urmApplication.getURMMeasureResult(
+        urmMeasureType: URMMeasureType.URMTraceCurvature,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: srcDPoints,
+      );
+      if (result != null) {
+        print(
+            "URM Measure curvature: ${result.resultData} nums: ${result.resultDPoints?.length}");
+        final feature = ref.feature!;
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.SRCurvature) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.resultData, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 42 - 0
lib/process/calcuators/urm_calcuators/urm_den.dart

@@ -0,0 +1,42 @@
+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_den.dart';
+
+class URMDenCal extends Calculator<URMDenAbstract, double> {
+  URMDenCal(URMDenAbstract ref) : super(ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final a1 = _pickChildValue(ref.child1);
+    final a2 = _pickChildValue(ref.child2);
+    final a3 = _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;
+  }
+}

+ 68 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_den_measure.dart

@@ -0,0 +1,68 @@
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMEllipseDenMeasureCal extends URMEllipseMeasureCal {
+  URMEllipseDenMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenMeasure,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+
+      if (result != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 82 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_den_vel_measure.dart

@@ -0,0 +1,82 @@
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMEllipseDenVelMeasureCal extends URMEllipseMeasureCal {
+  URMEllipseDenVelMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenVelMeasure,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(velResult.varianceVel), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 49 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_density.dart

@@ -0,0 +1,49 @@
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMEllipseDensityCal extends URMEllipseMeasureCal {
+  URMEllipseDensityCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDen,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != 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);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 49 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_fractal_dim.dart

@@ -0,0 +1,49 @@
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMEllipseFractalDimCal extends URMEllipseMeasureCal {
+  URMEllipseFractalDimCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMFracetalDim,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 24 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_hist.dart

@@ -0,0 +1,24 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMEllipseHistCal extends URMEllipseMeasureCal {
+  URMEllipseHistCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 77 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_measure.dart

@@ -0,0 +1,77 @@
+import 'dart:ui';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_ellipse_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+import '../calculator.dart';
+
+class URMEllipseMeasureCal extends Calculator<URMEllipseMeasure, double> {
+  URMEllipseMeasureCal(
+    URMEllipseMeasure ref,
+  ) : super(ref);
+  URMSimpleMeasureParams? param;
+  @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;
+    Size urmResultSize = const Size(0, 0);
+
+    try {
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      final URMApplication urmApplication = ref.application as URMApplication;
+      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
+          urmApplication.resultHeight.toDouble());
+
+      // 添加入参
+      List<UrmPoint> srcDPoints = [];
+      for (var point in ref.feature!.innerPoints) {
+        srcDPoints.add(urmApplication.localToView(point));
+      }
+
+      param = URMSimpleMeasureParams(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: srcDPoints,
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 68 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_perfusion.dart

@@ -0,0 +1,68 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:ui' as ui;
+
+class URMEllipsePerfusionCal extends URMEllipseMeasureCal {
+  URMEllipsePerfusionCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMPerfusion,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+
+      if (outResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMPerfusionIndex) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, outResult.resultData, output.unit);
+
+            if (feature is! EllipsePerfusionImageFeature) return;
+            if (outResult.perfusionImgBase64 != null) {
+              feature.perfusionImg =
+                  await loadImageFromBase64(outResult.perfusionImgBase64!);
+              feature.perfusionScaleDRect = urmApplication.viewToLocalRect(
+                outResult.perfusionScaleDRect!,
+              );
+            }
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+
+  Future<ui.Image> loadImageFromBase64(String base64String) async {
+    Uint8List bytes = base64Decode(base64String);
+    ui.Codec codec = await ui.instantiateImageCodec(bytes);
+    ui.FrameInfo frameInfo = await codec.getNextFrame();
+    return frameInfo.image;
+  }
+}

+ 64 - 0
lib/process/calcuators/urm_calcuators/urm_ellipse_vel_measure.dart

@@ -0,0 +1,64 @@
+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/calcuators/urm_calcuators/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'dart:math' as math;
+
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMEllipseVelMeasureCal extends URMEllipseMeasureCal {
+  URMEllipseVelMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMVelMeasure,
+        rOIType: URMROIType.URMEllipse,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(velResult.varianceVel), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 66 - 0
lib/process/calcuators/urm_calcuators/urm_location_vel.dart

@@ -0,0 +1,66 @@
+import 'dart:ui';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/primitives/location.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+import '../calculator.dart';
+
+class URMLocationVelCal extends Calculator<Location, double> {
+  URMLocationVelCal(Location ref) : super(ref);
+  URMSimpleMeasureParams? param;
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    if (ref.feature == null) return;
+    try {
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      handleURMSimpleMeasureParams();
+
+      Size urmResultSize = const Size(0, 0);
+      final URMApplication urmApplication = ref.application as URMApplication;
+      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
+          urmApplication.resultHeight.toDouble());
+      final point = ref.feature!.point.clone();
+      final startPoint = point.scale2Size(urmResultSize);
+
+      String description = "URM\n Calculating...";
+      updateStringValue(description);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+
+  /// 处理获取到的URM参数
+  void handleURMSimpleMeasureParams() {
+    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 point = ref.feature!.point.clone();
+      final startPoint = point.scale2Size(urmResultSize);
+
+      param = URMSimpleMeasureParams(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: [UrmPoint(x: startPoint.x, y: startPoint.y)],
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 0 - 216
lib/process/calcuators/urm_calcuators/urm_rect.dart

@@ -1,216 +0,0 @@
-import 'dart:math';
-import 'dart:ui';
-
-import 'package:fis_common/logger/logger.dart';
-import 'package:fis_jsonrpc/rpc.dart';
-import 'package:fis_measure/interfaces/date_types/point.dart';
-import 'package:fis_measure/interfaces/process/calculators/values.dart';
-import 'package:fis_measure/interfaces/process/items/terms.dart';
-import 'package:fis_measure/interfaces/process/items/types.dart';
-import 'package:fis_measure/process/items/item.dart';
-import 'package:fis_measure/process/primitives/area_abstract.dart';
-import 'package:fis_measure/process/primitives/combos/urm_den_combo.dart';
-import 'package:fis_measure/process/primitives/combos/urm_sr_roi_rect_combo.dart';
-import 'package:fis_measure/process/primitives/combos/urm_vel_combo.dart';
-import 'package:fis_measure/process/primitives/location.dart';
-import 'package:fis_measure/process/primitives/straightline.dart';
-import 'package:fis_measure/process/primitives/urm_rect.dart';
-import 'package:fis_measure/process/primitives/urm_straightline.dart';
-import 'package:fis_measure/process/workspace/urm/application.dart';
-import 'package:vid/us/vid_us_unit.dart';
-
-import '../calculator.dart';
-
-class URMRectCal extends Calculator<URMRect, double> {
-  URMRectCal(
-    URMRect ref, {
-    required this.type,
-  }) : super(ref);
-  final String type;
-  @override
-  void calculate() {}
-
-  @override
-  Future<void> calculateAsync() async {
-    if (ref.feature == null) return;
-    Size urmResultSize = const Size(0, 0);
-    try {
-      if (ref.application is! URMApplication) {
-        return;
-      }
-      final URMApplication urmApplication = ref.application as URMApplication;
-      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
-          urmApplication.resultHeight.toDouble());
-      final p1 = ref.feature!.startPoint;
-      final p2 = ref.feature!.endPoint;
-      //左上顶点
-      final leftTopPercent =
-          DPoint(p1.x < p2.x ? p1.x : p2.x, p1.y < p2.y ? p1.y : p2.y);
-      //右下顶点
-      final rightBottomPercent =
-          DPoint(p1.x > p2.x ? p1.x : p2.x, p1.y > p2.y ? p1.y : p2.y);
-
-      final startPoint = leftTopPercent.scale2Size(urmResultSize);
-      final endPoint = rightBottomPercent.scale2Size(urmResultSize);
-
-      String description = "URM\n Calculating...";
-      updateStringValue(description);
-
-      // switch (type) {
-      //   case MeasureTypes.DensitySR:
-      //   case MeasureTypes.SRRoiDensity:
-      //   case MeasureTypes.Roi1:
-      //   case MeasureTypes.Roi2:
-      //     await getSRRoiDensityResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.FractalDimSR:
-      //   case MeasureTypes.SRROIFractalDim:
-      //     await getSRRoiFractalDimResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.SRRoiVel:
-      //     await getSRRoiVelResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   case MeasureTypes.URMVelMeasure:
-      //     await getURMVelMeasureResult(urmApplication, startPoint, endPoint);
-      //     break;
-      //   default:
-      // }
-    } catch (e) {
-      logger.e('URM Measure error: $e');
-      return;
-    }
-  }
-
-  // //✅ URM 测量项 2
-  // Future<void> getSRRoiDensityResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiVelResult? result =
-  //         await app.getSRRoiVelResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if ([MeasureTerms.URMDenROI, MeasureTerms.URMDenFractalDim]
-  //             .contains(output.name)) {
-  //           output.unit = VidUsUnit.percent;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 3 未验证需要组合测量项
-  // Future<void> getSRRoiFractalDimResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiFractalDimResult? result =
-  //         await app.getSRRoiFractalDimResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMDenFractalDim) {
-  //           output.unit = VidUsUnit.None;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 7 未验证需要组合测量项
-  // Future<void> getSRRoiVelResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetSRRoiVelResult? result =
-  //         await app.getSRRoiVelResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       print("URM Measure output: ${result.output} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.SRRoiVel) {
-  //           output.unit = VidUsUnit.percent;
-  //           feature.updateFloatValue(output, result.output, output.unit);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-
-  // //✅ URM 测量项 9
-  // Future<void> getURMVelMeasureResult(
-  //     URMApplication app, DPoint startPoint, DPoint endPoint) async {
-  //   String description = "URM\n Measuring";
-  //   try {
-  //     GetURMVelMeasureResult? result =
-  //         await app.getURMVelMeasureResult(startPoint, endPoint);
-  //     if (result != null) {
-  //       // TODO 全部内容展示
-  //       print("URM Measure inMaxVel: ${result.inMaxVel} ");
-  //       final feature = ref.feature!;
-  //       for (var output in ref.meta.outputs) {
-  //         if (output.name == MeasureTerms.URMVelMax) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.maxVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelMin) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.minVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelMean) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, result.meanVel + app.urmMinVel, VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.URMVelStd) {
-  //           output.unit = VidUsUnit.mms;
-  //           feature.updateFloatValue(
-  //               output, sqrt(result.varianceVel), VidUsUnit.mms);
-  //         }
-  //         if (output.name == MeasureTerms.Area) {
-  //           output.unit = VidUsUnit.cm2;
-  //           feature.updateFloatValue(output, result.roiArea, VidUsUnit.cm2);
-  //         }
-  //       }
-  //     } else {
-  //       throw Exception("URM Measure API error");
-  //     }
-  //   } catch (e) {
-  //     description = " ";
-  //     updateStringValue(description);
-
-  //     return;
-  //   }
-  //   ref.application.updateRenderReady.emit(this, null);
-  // }
-}

+ 68 - 0
lib/process/calcuators/urm_calcuators/urm_rect_den_measure.dart

@@ -0,0 +1,68 @@
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMRectDenMeasureCal extends URMRectMeasureCal {
+  URMRectDenMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenMeasure,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+
+      if (result != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 82 - 0
lib/process/calcuators/urm_calcuators/urm_rect_den_vel_measure.dart

@@ -0,0 +1,82 @@
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMRectDenVelMeasureCal extends URMRectMeasureCal {
+  URMRectDenVelMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenVelMeasure,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(velResult.varianceVel), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 49 - 0
lib/process/calcuators/urm_calcuators/urm_rect_density.dart

@@ -0,0 +1,49 @@
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMRectDensityCal extends URMRectMeasureCal {
+  URMRectDensityCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDen,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != 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);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 52 - 0
lib/process/calcuators/urm_calcuators/urm_rect_fractal_dim.dart

@@ -0,0 +1,52 @@
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMRectFractalDimCal extends URMRectMeasureCal {
+  URMRectFractalDimCal(URMRectMeasure ref) : super(ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMFracetalDim,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 28 - 0
lib/process/calcuators/urm_calcuators/urm_rect_hist.dart

@@ -0,0 +1,28 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMRectHistCal extends URMRectMeasureCal {
+  URMRectHistCal(URMRectMeasure ref) : super(ref);
+  @override
+  URMSimpleMeasureParams? param;
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 65 - 0
lib/process/calcuators/urm_calcuators/urm_rect_measure.dart

@@ -0,0 +1,65 @@
+import 'dart:ui';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+import '../calculator.dart';
+
+class URMRectMeasureCal extends Calculator<URMRectMeasure, double> {
+  URMRectMeasureCal(
+    URMRectMeasure ref,
+  ) : super(ref);
+  URMSimpleMeasureParams? param;
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    if (ref.feature == null) return;
+    try {
+      Size urmResultSize = const Size(0, 0);
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      final URMApplication urmApplication = ref.application as URMApplication;
+      urmResultSize = Size(urmApplication.resultWidth.toDouble(),
+          urmApplication.resultHeight.toDouble());
+      final p1 = ref.feature!.startPoint;
+      final p2 = ref.feature!.endPoint;
+
+      String description = "URM\n Calculating...";
+      updateStringValue(description);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+
+  /// 处理获取到的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.URMCurvature,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: [
+          urmApplication.localToView(p1),
+          urmApplication.localToView(p2)
+        ],
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 69 - 0
lib/process/calcuators/urm_calcuators/urm_rect_perfusion.dart

@@ -0,0 +1,69 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'dart:ui' as ui;
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMRectPerfusionCal extends URMRectMeasureCal {
+  URMRectPerfusionCal(URMRectMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMPerfusion,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+
+      if (outResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMPerfusionIndex) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, outResult.resultData, output.unit);
+
+            if (feature is! RectPerfusionImageFeature) return;
+            if (outResult.perfusionImgBase64 != null) {
+              feature.perfusionImg =
+                  await loadImageFromBase64(outResult.perfusionImgBase64!);
+              feature.perfusionScaleDRect = urmApplication.viewToLocalRect(
+                outResult.perfusionScaleDRect!,
+              );
+            }
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+
+  Future<ui.Image> loadImageFromBase64(String base64String) async {
+    Uint8List bytes = base64Decode(base64String);
+    ui.Codec codec = await ui.instantiateImageCodec(bytes);
+    ui.FrameInfo frameInfo = await codec.getNextFrame();
+    return frameInfo.image;
+  }
+}

+ 65 - 0
lib/process/calcuators/urm_calcuators/urm_rect_vel_measure.dart

@@ -0,0 +1,65 @@
+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/calcuators/urm_calcuators/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMRectVelMeasureCal extends URMRectMeasureCal {
+  URMRectVelMeasureCal(URMRectMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMVelMeasure,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+      var velResult = outResult?.velMeasureResult;
+
+      if (result != null && velResult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, velResult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(velResult.varianceVel), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 69 - 0
lib/process/calcuators/urm_calcuators/urm_shell_den_measure.dart

@@ -0,0 +1,69 @@
+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/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+import 'dart:math' as math;
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMShellDenMeasureCal extends URMShellMeasureCal {
+  URMShellDenMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenMeasure,
+        rOIType: URMROIType.URMShell,
+        srcDPoints: param!.srcDPoints,
+      );
+      var result = outResult?.denMeasureResult;
+
+      if (result != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 88 - 0
lib/process/calcuators/urm_calcuators/urm_shell_den_vel_measure.dart

@@ -0,0 +1,88 @@
+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/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMShellDenVelMeasureCal extends URMShellMeasureCal {
+  URMShellDenVelMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenVelMeasure,
+        rOIType: URMROIType.URMShell,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      URMDenMeasureResult? denresult = outresult.denMeasureResult;
+      URMVelMeasureResult? velresult = outresult.velMeasureResult;
+      if (denresult != null && velresult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMDenROI) {
+            output.unit = VidUsUnit.percent;
+            feature.updateFloatValue(
+                output, denresult.roiDen * 100, output.unit);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, denresult.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, denresult.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.varianceDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(
+                output, velresult.varianceVel, output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, velresult.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 44 - 0
lib/process/calcuators/urm_calcuators/urm_shell_density_measure.dart

@@ -0,0 +1,44 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMShellDensityMeasureCal extends URMShellMeasureCal {
+  URMShellDensityMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDen,
+        rOIType: URMROIType.URMShell,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      final feature = ref.feature!;
+
+      final first = ref.meta.outputs.first;
+      first.unit = VidUsUnit.percent;
+
+      feature.updateFloatValue(first, outresult.resultData * 100, first.unit);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 26 - 0
lib/process/calcuators/urm_calcuators/urm_shell_fractal_dim_measure.dart

@@ -0,0 +1,26 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMShellFractalDimMeasureCal extends URMShellMeasureCal {
+  URMShellFractalDimMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 26 - 0
lib/process/calcuators/urm_calcuators/urm_shell_hist_measure.dart

@@ -0,0 +1,26 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMShellHistMeasureCal extends URMShellMeasureCal {
+  URMShellHistMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 70 - 0
lib/process/calcuators/urm_calcuators/urm_shell_measure.dart

@@ -0,0 +1,70 @@
+import 'dart:ui';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+import '../calculator.dart';
+
+class URMShellMeasureCal extends Calculator<URMShellMeasure, double> {
+  URMShellMeasureCal(
+    URMShellMeasure ref,
+  ) : super(ref);
+  URMSimpleMeasureParams? param;
+  @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<DPoint> points = ref.feature!.innerPoints
+          .map((e) => e.scale2Size(urmResultSize))
+          .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;
+
+    try {
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      // 添加入参
+      List<UrmPoint> srcDPoints = [];
+      for (var point in ref.feature!.innerPoints) {
+        srcDPoints.add(urmApplication.localToView(point));
+      }
+
+      param = URMSimpleMeasureParams(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.URMShell,
+        srcDPoints: srcDPoints,
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 26 - 0
lib/process/calcuators/urm_calcuators/urm_shell_perfusion_measure.dart

@@ -0,0 +1,26 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMShellPerfusionMeasureCal extends URMShellMeasureCal {
+  URMShellPerfusionMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 26 - 0
lib/process/calcuators/urm_calcuators/urm_shell_vel_measure.dart

@@ -0,0 +1,26 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+class URMShellVelMeasureCal extends URMShellMeasureCal {
+  URMShellVelMeasureCal(URMShellMeasure ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.handleURMSimpleMeasureParams();
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+
+      // TODO 规划入参,坐标系转换,调用接口,处理返回结果
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+}

+ 69 - 0
lib/process/calcuators/urm_calcuators/urm_straight_curvature_line.dart

@@ -0,0 +1,69 @@
+import 'dart:ui';
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/primitives/straightline.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_straight_curvature_line_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import '../calculator.dart';
+
+/// 自动弯曲度测量
+class URMStraightCurvatureLineCal extends Calculator<StraightLine, double> {
+  URMStraightCurvatureLineCal(StraightLine ref) : super(ref);
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    try {
+      if (ref.feature == null) return;
+      if (ref.application is! URMApplication) return;
+      final URMApplication urmApplication = ref.application as URMApplication;
+      final p1 = ref.feature!.startPoint;
+      final p2 = ref.feature!.endPoint;
+
+      await calculteURMCurvature(urmApplication, p1, p2);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+  }
+
+  // // ✅ URM 测量项 1
+  Future<void> calculteURMCurvature(
+      URMApplication app, DPoint startPoint, DPoint endPoint) async {
+    String description = "URM\n Measuring";
+    try {
+      URMMeasureProcessResult? result = await app.getURMMeasureResult(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.placeHolder_0,
+        srcDPoints: [
+          app.localToView(startPoint),
+          app.localToView(endPoint),
+        ],
+      );
+      if (result != null) {
+        print(
+            "URM Measure curvature: ${result.resultData} nums: ${result.resultDPoints?.length}");
+        final feature = ref.feature!;
+        if (feature is! StraightCurvatureFeature) return;
+        feature.autoLinePoints = app.urmPointsToDPoints(result.resultDPoints);
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.SRCurvature) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.resultData, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      description = " ";
+      updateStringValue(description);
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 71 - 0
lib/process/calcuators/urm_calcuators/urm_trace_den_measure.dart

@@ -0,0 +1,71 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/services/aIDiagnosis.m.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+import 'dart:math' as math;
+
+class URMTraceDenMeasureCal extends URMTraceMeasureCal {
+  URMTraceDenMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenMeasure,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      URMDenMeasureResult? result = outresult.denMeasureResult;
+
+      if (result != 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);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, result.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, math.sqrt(result.varianceDensity), output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 86 - 0
lib/process/calcuators/urm_calcuators/urm_trace_den_vel_measure.dart

@@ -0,0 +1,86 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/services/aIDiagnosis.m.dart';
+import 'package:fis_measure/interfaces/process/items/terms.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTraceDenVelMeasureCal extends URMTraceMeasureCal {
+  URMTraceDenVelMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDenVelMeasure,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      URMDenMeasureResult? denresult = outresult.denMeasureResult;
+      URMVelMeasureResult? velresult = outresult.velMeasureResult;
+      if (denresult != null && velresult != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMDenROI) {
+            output.unit = VidUsUnit.percent;
+            feature.updateFloatValue(
+                output, denresult.roiDen * 100, output.unit);
+          } else if (output.name == MeasureTerms.URMDenFractalDim) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.roiFractalDim, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMax) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, denresult.maxDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMin) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(output, denresult.minDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenMean) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.meanDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMDenStd) {
+            output.unit = VidUsUnit.None;
+            feature.updateFloatValue(
+                output, denresult.varianceDensity, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, velresult.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(
+                output, velresult.varianceVel, output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, velresult.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 42 - 0
lib/process/calcuators/urm_calcuators/urm_trace_density.dart

@@ -0,0 +1,42 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTraceDensityCal extends URMTraceMeasureCal {
+  URMTraceDensityCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMDen,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      final feature = ref.feature!;
+
+      final first = ref.meta.outputs.first;
+      first.unit = VidUsUnit.percent;
+
+      feature.updateFloatValue(first, outresult.resultData * 100, first.unit);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 42 - 0
lib/process/calcuators/urm_calcuators/urm_trace_fractal_dim.dart

@@ -0,0 +1,42 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTraceFractalDimCal extends URMTraceMeasureCal {
+  URMTraceFractalDimCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMFracetalDim,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      final feature = ref.feature!;
+
+      final first = ref.meta.outputs.first;
+      first.unit = VidUsUnit.None;
+
+      feature.updateFloatValue(first, outresult.resultData, first.unit);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 45 - 0
lib/process/calcuators/urm_calcuators/urm_trace_hist.dart

@@ -0,0 +1,45 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTraceHistCal extends URMTraceMeasureCal {
+  URMTraceHistCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMHist,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      final feature = ref.feature!;
+
+      /// TODO 只转数据类型还不够,需要将 视图区域百分比坐标转为全图百分比坐标
+
+      final first = ref.meta.outputs.first;
+      first.unit = VidUsUnit.None;
+
+      /// 超声机上面注释掉了
+      // feature.updateFloatValue(first, outresult.resultData, first.unit);
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 70 - 0
lib/process/calcuators/urm_calcuators/urm_trace_measure.dart

@@ -0,0 +1,70 @@
+import 'dart:ui';
+
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+
+import 'package:fis_measure/process/primitives/urm_measure/urm_trace_measure.dart';
+// import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+
+import '../calculator.dart';
+
+class URMTraceMeasureCal extends Calculator<URMTraceMeasure, double> {
+  URMTraceMeasureCal(
+    URMTraceMeasure ref,
+  ) : super(ref);
+  URMSimpleMeasureParams? param;
+  @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<DPoint> points = ref.feature!.innerPoints
+          .map((e) => e.scale2Size(urmResultSize))
+          .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;
+
+    try {
+      if (ref.application is! URMApplication) {
+        return;
+      }
+      final URMApplication urmApplication = ref.application as URMApplication;
+
+      // 添加入参
+      List<UrmPoint> srcDPoints = [];
+      for (var point in ref.feature!.innerPoints) {
+        srcDPoints.add(urmApplication.localToView(point));
+      }
+
+      param = URMSimpleMeasureParams(
+        urmMeasureType: URMMeasureType.URMCurvature,
+        rOIType: URMROIType.URMRect,
+        srcDPoints: srcDPoints,
+      );
+    } catch (e) {
+      logger.e('URM handleURMSimpleMeasureParams error: $e');
+    }
+  }
+}

+ 66 - 0
lib/process/calcuators/urm_calcuators/urm_trace_perfusion.dart

@@ -0,0 +1,66 @@
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'dart:ui' as ui;
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTracePerfusionCal extends URMTraceMeasureCal {
+  URMTracePerfusionCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMPerfusion,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      final feature = ref.feature!;
+
+      if (ref.meta.outputs.isNotEmpty) {
+        final first = ref.meta.outputs.first;
+        first.unit = VidUsUnit.None;
+
+        feature.updateFloatValue(first, outresult.resultData, first.unit);
+
+        if (feature is! TracePerfusionImageFeature) return;
+        if (outresult.perfusionImgBase64 != null) {
+          feature.perfusionImg =
+              await loadImageFromBase64(outresult.perfusionImgBase64!);
+          feature.leftTopPoint = urmApplication
+              .viewToLocal(outresult.perfusionScaleDRect!.topLeft!);
+          feature.rightBottomPoint = urmApplication
+              .viewToLocal(outresult.perfusionScaleDRect!.bottomRight!);
+        }
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+
+  Future<ui.Image> loadImageFromBase64(String base64String) async {
+    Uint8List bytes = base64Decode(base64String);
+    ui.Codec codec = await ui.instantiateImageCodec(bytes);
+    ui.FrameInfo frameInfo = await codec.getNextFrame();
+    return frameInfo.image;
+  }
+}

+ 63 - 0
lib/process/calcuators/urm_calcuators/urm_trace_vel_measure.dart

@@ -0,0 +1,63 @@
+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/calcuators/urm_calcuators/urm_trace_measure.dart';
+import 'package:fis_measure/process/workspace/urm/application.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+class URMTraceVelMeasureCal extends URMTraceMeasureCal {
+  URMTraceVelMeasureCal(super.ref);
+
+  @override
+  void calculate() {}
+
+  @override
+  Future<void> calculateAsync() async {
+    super.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.URMVelMeasure,
+        rOIType: URMROIType.URMTrace,
+        srcDPoints: param!.srcDPoints,
+      );
+      if (outresult == null) {
+        return;
+      }
+      URMVelMeasureResult? result = outresult.velMeasureResult;
+
+      if (result != null) {
+        final feature = ref.feature!;
+
+        for (var output in ref.meta.outputs) {
+          if (output.name == MeasureTerms.URMVelMax) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, result.maxVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMin) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, result.minVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelMean) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, result.meanVel, output.unit);
+          } else if (output.name == MeasureTerms.URMVelStd) {
+            output.unit = VidUsUnit.mms;
+            feature.updateFloatValue(output, result.varianceVel, output.unit);
+          } else if (output.name == MeasureTerms.Area) {
+            output.unit = VidUsUnit.cm2;
+            feature.updateFloatValue(output, result.roiArea, output.unit);
+          }
+        }
+      } else {
+        throw Exception("URM Measure API error");
+      }
+    } catch (e) {
+      logger.e('URM Measure error: $e');
+      return;
+    }
+    ref.application.updateRenderReady.emit(this, null);
+  }
+}

+ 42 - 0
lib/process/calcuators/urm_calcuators/urm_vel.dart

@@ -0,0 +1,42 @@
+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_vel.dart';
+
+class URMVelCal extends Calculator<URMVelAbstract, double> {
+  URMVelCal(URMVelAbstract ref) : super(ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final a1 = _pickChildValue(ref.child1);
+    final a2 = _pickChildValue(ref.child2);
+    final a3 = _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;
+  }
+}

+ 111 - 64
lib/process/items/factory.dart

@@ -19,9 +19,10 @@ 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/combos/urm_den.dart';
-import 'package:fis_measure/process/primitives/combos/urm_vel.dart';
-import 'package:fis_measure/process/primitives/combos/urm_sr_roi_rect.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_vel.dart';
 import 'package:fis_measure/process/primitives/detection.dart';
 import 'package:fis_measure/process/primitives/ellipse.dart';
 import 'package:fis_measure/process/primitives/empty.dart';
@@ -37,10 +38,14 @@ import 'package:fis_measure/process/primitives/spline.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 import 'package:fis_measure/process/primitives/trace.dart';
 import 'package:fis_measure/process/primitives/twoline_angle.dart';
-import 'package:fis_measure/process/primitives/urm_location.dart';
-import 'package:fis_measure/process/primitives/urm_rect.dart';
-import 'package:fis_measure/process/primitives/urm_straightline.dart';
-import 'package:fis_measure/process/primitives/urm_trace.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_ellipse_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_location_vel.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_shell_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_vessel_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_curvature_trace_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_curve_curvature_line_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_straight_curvature_line_measure.dart';
 
 /// 测量项创建器
 ///
@@ -247,65 +252,107 @@ class MeasureItemFactory {
     _singleton._register(
         MeasureTypes.IntimaDetection, CarotidDetection.createDetectionRect);
 
-    // URM
-    _singleton._register(
-      MeasureTypes.SRStraightCurvatureLineMeasure,
-      URMStraightLine.createURMStraightline,
-    );
-    _singleton._register(
-      MeasureTypes.SRRoiDensity,
-      URMRect.createMeasureRect,
-    );
-    _singleton._register(
-      MeasureTypes.SRROIFractalDim,
-      URMRect.createMeasureRect,
-    );
-    _singleton._register(
-      MeasureTypes.SRTraceDensity,
-      URMTrace.createURMTrace,
-    );
-    _singleton._register(
-      MeasureTypes.SRTraceFractalDim,
-      URMTrace.createURMTrace,
-    );
-    _singleton._register(
-      MeasureTypes.SRLoactionVel,
-      URMLoaction.createURMLocation,
-    );
-    _singleton._register(
-      MeasureTypes.SRRoiVel,
-      URMRect.createMeasureRect,
-    );
-    _singleton._register(
-      MeasureTypes.URMRectDenMeasure,
-      URMStraightLine.createURMStraightline,
-    );
-    _singleton._register(
-      MeasureTypes.URMVesselMeasure,
-      URMStraightLine.createURMStraightline,
-    );
-    _singleton._register(
-      MeasureTypes.URMRectVelMeasure,
-      URMRect.createMeasureRect,
-    );
-    _singleton._register(
-      MeasureTypes.URMVel,
-      URMVel.crateURMVel,
-    );
-    _singleton._register(
-      MeasureTypes.URMDen,
-      URMDen.crateURMDen,
-    );
+    // URM 超分辨
 
-    _singleton._register(
-      MeasureTypes.DensityTwoSRRoiRect,
-      URMTwoSRRoiRect.crateURMTwoSRRoiRect,
-    );
+    // _singleton._register(MeasureTypes.URMCurvature,
+    //     URMCurvatureMeasure.CreateURMCurvatureMeasure);
+    _singleton._register(MeasureTypes.URMStraightCurvatureLineMeasure,
+        URMStraightCurvatureLineMeasure.createURMStraightCurvatureLineMeasure);
+    _singleton._register(MeasureTypes.URMCurveCurvatureLineMeasure,
+        URMCurveCurvatureLineMeasure.createURMCurveCurvatureLineMeasure);
+    _singleton._register(MeasureTypes.URMCurvatureTraceMeasure,
+        URMCurvatureTraceMeasure.createURMCurvatureTraceMeasure);
+    _singleton._register(
+        MeasureTypes.URMRectDensity, URMRectMeasure.createURMRectDensity);
 
-    _singleton._register(
-      MeasureTypes.FractalDimTwoSRRoiRect,
-      URMTwoSRRoiRect.crateURMTwoSRRoiRect,
-    );
+    /// TODO
+    _singleton._register(
+        MeasureTypes.URMTraceDensity, URMTraceMeasure.createURMTraceDensity);
+    _singleton._register(MeasureTypes.URMEllipseDensity,
+        URMEllipseMeasure.createURMEllipseDensity);
+    _singleton._register(MeasureTypes.URMLocationVel,
+        URMLocationVelMeasure.createURMLocationVelMeasure);
+    _singleton._register(
+        MeasureTypes.URMRectFractalDim, URMRectMeasure.createURMRectFractalDim);
+    _singleton._register(MeasureTypes.URMTraceFractalDim,
+        URMTraceMeasure.createURMTraceFractalDim);
+    _singleton._register(MeasureTypes.URMEllipseFractalDim,
+        URMEllipseMeasure.createURMEllipseFractalDim);
+    // _singleton._register(
+    //     MeasureTypes.FractalDimURM, FractalDimURM.CreateFractalDimURM);
+    // _singleton._register(MeasureTypes.DensityURM, DensityURM.CreateDensityURM);
+    _singleton._register(MeasureTypes.URMDen, URMDen.create);
+    _singleton._register(MeasureTypes.URMVel, URMVel.create);
+
+    // _singleton._register(MeasureTypes.AutoUterusVertical,
+    //     AutoUterusVertical.CreateAutoUterusVertical);
+    // _singleton._register(
+    //     MeasureTypes.AutoUterusCross, AutoUterusCross.CreateAutoUterusCross);
+    _singleton._register(MeasureTypes.URMDensityRation, AbRatio.create);
+    _singleton._register(MeasureTypes.URMFractalDimRation, AbRatio.create);
+    _singleton._register(
+        MeasureTypes.URMRectDenMeasure, URMRectMeasure.createURMRectDenMeasure);
+    _singleton._register(MeasureTypes.URMTraceDenMeasure,
+        URMTraceMeasure.createURMTraceDenMeasure);
+    _singleton._register(MeasureTypes.URMEllipseDenMeasure,
+        URMEllipseMeasure.createURMEllipseDenMeasure);
+    _singleton._register(
+        MeasureTypes.URMRectVelMeasure, URMRectMeasure.createURMRectVelMeasure);
+    _singleton._register(MeasureTypes.URMTraceVelMeasure,
+        URMTraceMeasure.createURMTraceVelMeasure);
+    _singleton._register(MeasureTypes.URMEllipseVelMeasure,
+        URMEllipseMeasure.createURMEllipseVelMeasure);
+    ////////// 分界线 ///////////
+    // _singleton._register(MeasureTypes.URMDensityMeasure,
+    //     URMDensityMeasure.CreateURMDensityMeasure);
+    // _singleton._register(
+    //     MeasureTypes.URMVelMeasure, URMVelMeasure.CreateURMVelMeasure);
+    // _singleton._register(MeasureTypes.URMDensityVelMeasure,
+    //     URMDensityVelMeasure.CreateURMDensityVelMeasure);
+    _singleton._register(
+        MeasureTypes.URMVesselMeasure, URMVesselMeasure.createURMVesselMeasure);
+    _singleton._register(
+        MeasureTypes.URMRectPerfusion, URMRectMeasure.createURMRectPerfusion);
+    _singleton._register(MeasureTypes.URMTracePerfusion,
+        URMTraceMeasure.createURMTracePerfusion);
+    _singleton._register(MeasureTypes.URMEllipsePerfusion,
+        URMEllipseMeasure.createURMEllipsePerfusion);
+
+    // =============== 超声机还未完成 Hist 开始================
+    // _singleton._register(
+    //   MeasureTypes.URMRectHist, URMRectMeasure.createURMRectHist);
+    // _singleton._register(
+    //     MeasureTypes.URMTraceHist, URMTraceMeasure.createURMTraceHist);
+    // _singleton._register(
+    //     MeasureTypes.URMEllipseHist, URMEllipseMeasure.createURMEllipseHist);
+    // _singleton._register(MeasureTypes.URMShellHistMeasure,
+    //     URMShellMeasure.createURMShellHistMeasure);
+    // =============== 超声机还未完成 结束================
+
+    // _singleton._register(MeasureTypes.URMPerfusionMeasure,
+    //     URMPerfusionMeasure.CreateURMPerfusionMeasure);
+    // _singleton._register(
+    //     MeasureTypes.URMHistMeasure, URMHistMeasure.CreateURMHistMeasure);
+
+    _singleton._register(MeasureTypes.URMShellDenMeasure,
+        URMShellMeasure.createURMShellDenMeasure);
+    _singleton._register(MeasureTypes.URMShellVelMeasure,
+        URMShellMeasure.createURMShellVelMeasure);
+    _singleton._register(MeasureTypes.URMShellDensityMeasure,
+        URMShellMeasure.createURMShellDensityMeasure);
+    _singleton._register(MeasureTypes.URMShellFractalDimMeasure,
+        URMShellMeasure.createURMShellFractalDimMeasure);
+    _singleton._register(MeasureTypes.URMShellPerfusionMeasure,
+        URMShellMeasure.createURMShellPerfusionMeasure);
+
+    _singleton._register(MeasureTypes.URMRectDenVelMeasure,
+        URMRectMeasure.createURMRectDenVelMeasure);
+    _singleton._register(MeasureTypes.URMTraceDenVelMeasure,
+        URMTraceMeasure.createURMTraceDenVelMeasure);
+    _singleton._register(MeasureTypes.URMEllipseDenVelMeasure,
+        URMEllipseMeasure.createURMEllipseDenVelMeasure);
+    _singleton._register(MeasureTypes.URMShellDenVelMeasure,
+        URMShellMeasure.createURMShellDenVelMeasure);
 
     _singleton._register(
       MeasureTypes.SemiautoTrace,

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

@@ -26,6 +26,7 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
   List<ValueBase> _values = [];
   late final measureData = Get.find<MeasureDataController>();
   int _activeIndex = -1;
+  Rect? zoomRect; // 当前缩放区域的归一化坐标
 
   @override
   int? frameIndex;
@@ -181,11 +182,29 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
 
   @protected
   DPoint convert2ViewPoint(Size size, DPoint logicPoint) {
+    if (zoomRect != null) {
+      DPoint zoomedLogicPoint = getZoomedPoint(zoomRect!, logicPoint);
+      final x = size.width * zoomedLogicPoint.x;
+      final y = size.height * zoomedLogicPoint.y;
+      return DPoint(x, y);
+    }
     final x = size.width * logicPoint.x;
     final y = size.height * logicPoint.y;
     return DPoint(x, y);
   }
 
+  // 获取点 p 在 Rect r 中的归一化坐标
+  DPoint getZoomedPoint(Rect r, DPoint p) {
+    double xPctInR = (p.x - r.left) / r.width;
+    double yPctInR = (p.y - r.top) / r.height;
+    return DPoint(xPctInR, yPctInR);
+  }
+
+  @override
+  void setZoomRect(Rect? rect) {
+    zoomRect = rect;
+  }
+
   /// 画序号
   ///
   /// [text] 自定义序号内容

+ 0 - 18
lib/process/primitives/combos/urm_den_combo.dart

@@ -1,18 +0,0 @@
-import 'package:fis_measure/interfaces/process/items/item_metas.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';
-
-abstract class URMDenAbstract<T extends MeasureItemFeature>
-    extends TopMeasureItem<T> {
-  URMDenAbstract(ItemMeta meta) : super(meta);
-
-  MeasureItem get child1;
-  MeasureItem get child2;
-  MeasureItem get child3;
-  MeasureItem get child4;
-  MeasureItem get child5;
-
-  @override
-  bool get finishAfterUnactive => true;
-}

+ 8 - 8
lib/process/primitives/combos/urm_sr_roi_rect.dart

@@ -3,29 +3,29 @@ import 'package:fis_measure/interfaces/process/items/item_metas.dart';
 import 'package:fis_measure/process/calcuators/urm.dart';
 import 'package:fis_measure/process/items/top_item_feature.dart';
 import 'package:fis_measure/process/primitives/combos/urm_sr_roi_rect_combo.dart';
-import 'package:fis_measure/process/primitives/urm_rect.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
 
 class URMTwoSRRoiRect extends URMTwoSRRoiRectAbstract<URMVelFeature> {
   static const String _area1Key = "Roi1";
   static const String _area2Key = "Roi2";
 
-  late final URMRect a1;
-  late final URMRect a2;
+  late final URMRectMeasure a1;
+  late final URMRectMeasure a2;
 
   URMTwoSRRoiRect(ItemMeta meta) : super(meta) {
     final metaA1 = meta.getChildByName(_area1Key)!;
     final metaA2 = meta.getChildByName(_area2Key)!;
-    a1 = URMRect.createMeasureRect(metaA1, this);
-    a2 = URMRect.createMeasureRect(metaA2, this);
+    a1 = URMRectMeasure.createMeasureRect(metaA1, this);
+    a2 = URMRectMeasure.createMeasureRect(metaA2, this);
     childItems.add(a1);
     childItems.add(a2);
   }
 
   @override
-  URMRect get child1 => a1;
+  URMRectMeasure get child1 => a1;
 
   @override
-  URMRect get child2 => a2;
+  URMRectMeasure get child2 => a2;
 
   @override
   URMVelFeature buildFeature() => URMVelFeature(this);
@@ -36,7 +36,7 @@ class URMTwoSRRoiRect extends URMTwoSRRoiRectAbstract<URMVelFeature> {
   static URMTwoSRRoiRect crateURMTwoSRRoiRect(ItemMeta meta,
       [IMeasureItem? parent]) {
     var urmVel = URMTwoSRRoiRect(meta);
-    urmVel.calculator = URMSrDensityCal(urmVel);
+    // urmVel.calculator = URMSrDensityCal(urmVel);
 
     return urmVel;
   }

+ 0 - 16
lib/process/primitives/combos/urm_vel_combo.dart

@@ -1,16 +0,0 @@
-import 'package:fis_measure/interfaces/process/items/item_metas.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';
-
-abstract class URMVelAbstract<T extends MeasureItemFeature>
-    extends TopMeasureItem<T> {
-  URMVelAbstract(ItemMeta meta) : super(meta);
-
-  MeasureItem get child1;
-  MeasureItem get child2;
-  MeasureItem get child3;
-
-  @override
-  bool get finishAfterUnactive => true;
-}

+ 1 - 1
lib/process/primitives/urm_location.dart

@@ -14,7 +14,7 @@ class URMLoaction extends Location {
     IMeasureItem? parent,
   ]) {
     URMLoaction location = URMLoaction(meta, parent);
-    location.calculator = URMLocationCal(location);
+    // location.calculator = URMLocationCal(location);
     return location;
   }
 

+ 15 - 0
lib/process/primitives/urm_measure/ab_ratio.dart

@@ -0,0 +1,15 @@
+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';
+
+/// TODO 暂时这样写
+class AbRatio extends StraightLine {
+  AbRatio(super.meta, super.parent);
+
+  static AbRatio create(ItemMeta meta, [IMeasureItem? parent]) {
+    AbRatio measureVessel = AbRatio(meta, parent);
+    measureVessel.calculator = DistanceCal(measureVessel);
+    return measureVessel;
+  }
+}

+ 96 - 0
lib/process/primitives/urm_measure/urm_curvature_trace_measure.dart

@@ -0,0 +1,96 @@
+import 'package:fis_measure/interfaces/date_types/point.dart';
+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/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_curvature_trace_measure.dart';
+import 'package:fis_measure/process/primitives/area_abstract.dart';
+import 'package:fis_measure/process/primitives/trace.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+import 'package:fis_measure/utils/canvas.dart';
+import 'package:flutter/material.dart';
+
+class URMCurvatureTraceMeasure extends Trace {
+  URMCurvatureTraceMeasure(ItemMeta meta, IMeasureItem? parent)
+      : super(meta, parent);
+
+  static URMCurvatureTraceMeasure createURMCurvatureTraceMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMCurvatureTraceMeasure trace = URMCurvatureTraceMeasure(meta, parent);
+    trace.calculator = URMCurvatureTraceMeasureCal(trace);
+    trace.isClosed = false;
+    return trace;
+  }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (waitingResult) return false;
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      }
+    }
+
+    if (state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleMouseDownWhileWaiting(args);
+      }
+    } else if (state == ItemStates.running) {
+      if (args.pointType == PointInfoType.mouseUp) return false;
+
+      feature?.adopt(args);
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleFinish();
+      } else {
+        checkAutoSnap(args);
+      }
+    }
+    return true;
+  }
+
+  @override
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    feature = TraceCurvatureFeature(this);
+    if (args.hostVisualArea != null) {
+      feature!.hostVisualArea = args.hostVisualArea;
+    }
+    final point = args.toAreaLogicPoint();
+    feature!.adopt(point);
+    state = ItemStates.running;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
+}
+
+class TraceCurvatureFeature extends TraceFeature {
+  TraceCurvatureFeature(AreaItemAbstract refItem) : super(refItem);
+
+  List<DPoint> autoLinePoints = [];
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    super.paint(canvas, size);
+
+    /// 绘制 AutoLine 点集连线
+    /// 使用 convert2ViewPoint 转为像素坐标
+    final pixelPoints = autoLinePoints.map((e) {
+      return convert2ViewPoint(size, e).toOffset();
+    }).toList();
+    final greenLinePan = Paint()
+      ..color = Colors.green
+      ..isAntiAlias = true
+      ..strokeWidth = 2
+      ..style = PaintingStyle.stroke;
+    canvas.drawPointsLine(pixelPoints, greenLinePan);
+  }
+}

+ 60 - 0
lib/process/primitives/urm_measure/urm_curve_curvature_line_measure.dart

@@ -0,0 +1,60 @@
+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/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_curve_curvature_line.dart';
+import 'package:fis_measure/process/primitives/trace.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+
+class URMCurveCurvatureLineMeasure extends Trace {
+  URMCurveCurvatureLineMeasure(ItemMeta meta, IMeasureItem? parent)
+      : super(meta, parent);
+
+  static URMCurveCurvatureLineMeasure createURMCurveCurvatureLineMeasure(
+      ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMCurveCurvatureLineMeasure trace =
+        URMCurveCurvatureLineMeasure(meta, parent);
+    trace.calculator = URMCurveCurvatureLineCal(trace);
+    trace.isClosed = false;
+    return trace;
+  }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (waitingResult) return false;
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      }
+    }
+
+    if (state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleMouseDownWhileWaiting(args);
+      }
+    } else if (state == ItemStates.running) {
+      if (args.pointType == PointInfoType.mouseUp) return false;
+
+      feature?.adopt(args);
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleFinish();
+      } else {
+        checkAutoSnap(args);
+      }
+    }
+    return true;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
+}

+ 35 - 17
lib/process/primitives/combos/urm_den.dart → lib/process/primitives/urm_measure/urm_den.dart

@@ -1,22 +1,25 @@
 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_den.dart';
 import 'package:fis_measure/process/items/top_item_feature.dart';
-import 'package:fis_measure/process/primitives/combos/urm_den_combo.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
-import 'package:fis_measure/process/primitives/urm_rect.dart';
-import 'package:fis_measure/process/primitives/urm_straightline.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_rect_measure.dart';
+import 'package:fis_measure/process/primitives/urm_measure/urm_straight_curvature_line_measure.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_straightline.dart';
 
-class URMDen extends URMDenAbstract<URMVelFeature> {
+class URMDen extends URMDenAbstract<URMDenFeature> {
   static const String _area1Key = "SRCurvature";
   static const String _area2Key = "SRRoiDensity";
   static const String _area3Key = "SRROIFractalDim";
   static const String _area4Key = "Vessel diameter";
   static const String _area5Key = "Vessel distance";
 
-  late final URMStraightLine a1;
-  late final URMRect a2;
-  late final URMRect a3;
+  late final URMStraightCurvatureLineMeasure a1;
+  late final URMRectMeasure a2;
+  late final URMRectMeasure a3;
   late final StraightLine a4;
   late final StraightLine a5;
 
@@ -26,9 +29,10 @@ class URMDen extends URMDenAbstract<URMVelFeature> {
     final metaA3 = meta.getChildByName(_area3Key)!;
     final metaA4 = meta.getChildByName(_area4Key)!;
     final metaA5 = meta.getChildByName(_area5Key)!;
-    a1 = URMStraightLine.createURMStraightline(metaA1, this);
-    a2 = URMRect.createMeasureRect(metaA2, this);
-    a3 = URMRect.createMeasureRect(metaA3, this);
+    a1 = URMStraightCurvatureLineMeasure.createURMStraightCurvatureLineMeasure(
+        metaA1, this);
+    a2 = URMRectMeasure.createMeasureRect(metaA2, this);
+    a3 = URMRectMeasure.createMeasureRect(metaA3, this);
     a4 = StraightLine.createDistance(metaA4, this);
     a5 = StraightLine.createDistance(metaA5, this);
     childItems.add(a1);
@@ -42,10 +46,10 @@ class URMDen extends URMDenAbstract<URMVelFeature> {
   StraightLine get child1 => a1;
 
   @override
-  URMRect get child2 => a2;
+  URMRectMeasure get child2 => a2;
 
   @override
-  URMRect get child3 => a3;
+  URMRectMeasure get child3 => a3;
 
   @override
   StraightLine get child4 => a4;
@@ -54,12 +58,12 @@ class URMDen extends URMDenAbstract<URMVelFeature> {
   StraightLine get child5 => a5;
 
   @override
-  URMVelFeature buildFeature() => URMVelFeature(this);
+  URMDenFeature buildFeature() => URMDenFeature(this);
 
   @override
   void onCancelingOnce() {}
 
-  static URMDen crateURMDen(ItemMeta meta, [IMeasureItem? parent]) {
+  static URMDen create(ItemMeta meta, [IMeasureItem? parent]) {
     var urmDen = URMDen(meta);
     urmDen.calculator = URMDenCal(urmDen);
 
@@ -67,8 +71,22 @@ class URMDen extends URMDenAbstract<URMVelFeature> {
   }
 }
 
-class URMVelFeature extends TopMeasureItemFeature {
-  URMVelFeature(
+abstract class URMDenAbstract<T extends MeasureItemFeature>
+    extends TopMeasureItem<T> {
+  URMDenAbstract(ItemMeta meta) : super(meta);
+
+  MeasureItem get child1;
+  MeasureItem get child2;
+  MeasureItem get child3;
+  MeasureItem get child4;
+  MeasureItem get child5;
+
+  @override
+  bool get finishAfterUnactive => true;
+}
+
+class URMDenFeature extends TopMeasureItemFeature {
+  URMDenFeature(
     ITopMeasureItem refItem,
   ) : super(refItem);
 }

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

@@ -0,0 +1,186 @@
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+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/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_den_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_den_vel_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_density.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_fractal_dim.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_hist.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_perfusion.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_ellipse_vel_measure.dart';
+import 'package:fis_measure/process/primitives/ellipse.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+import 'package:flutter/material.dart';
+import 'dart:ui' as ui;
+
+class URMEllipseMeasure extends Ellipse {
+  URMEllipseMeasure(super.meta, super.parent);
+
+  static bool needPerfusion = false;
+
+  static URMEllipseMeasure createURMEllipseDenMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureEllipse = URMEllipseMeasure(meta, parent);
+    measureEllipse.calculator = URMEllipseDenMeasureCal(
+      measureEllipse,
+    );
+    return measureEllipse;
+  }
+
+  static URMEllipseMeasure createURMEllipseHist(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureHist = URMEllipseMeasure(meta, parent);
+    measureHist.calculator = URMEllipseHistCal(
+      measureHist,
+    );
+    return measureHist;
+  }
+
+  static URMEllipseMeasure createURMEllipseDenVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureEllipseDenVel = URMEllipseMeasure(meta, parent);
+    measureEllipseDenVel.calculator = URMEllipseDenVelMeasureCal(
+      measureEllipseDenVel,
+    );
+    return measureEllipseDenVel;
+  }
+
+  static URMEllipseMeasure createURMEllipseDensity(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureEllipseDensity = URMEllipseMeasure(meta, parent);
+    measureEllipseDensity.calculator = URMEllipseDensityCal(
+      measureEllipseDensity,
+    );
+    return measureEllipseDensity;
+  }
+
+  static URMEllipseMeasure createURMEllipseFractalDim(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureEllipseFractalDim =
+        URMEllipseMeasure(meta, parent);
+    measureEllipseFractalDim.calculator = URMEllipseFractalDimCal(
+      measureEllipseFractalDim,
+    );
+    return measureEllipseFractalDim;
+  }
+
+  static URMEllipseMeasure createURMEllipseVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMEllipseMeasure measureEllipseVel = URMEllipseMeasure(meta, parent);
+    measureEllipseVel.calculator = URMEllipseVelMeasureCal(
+      measureEllipseVel,
+    );
+
+    return measureEllipseVel;
+  }
+
+  static URMEllipseMeasure createURMEllipsePerfusion(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    needPerfusion = true;
+    URMEllipseMeasure measureEllipsePerfusion = URMEllipseMeasure(meta, parent);
+    measureEllipsePerfusion.calculator = URMEllipsePerfusionCal(
+      measureEllipsePerfusion,
+    );
+    return measureEllipsePerfusion;
+  }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      }
+    }
+
+    if (state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleMouseDownWhileWaiting(args);
+      }
+    } else if (state == ItemStates.running) {
+      if (feature == null) return false;
+      final f = feature! as EllipseFeature;
+      final activeIndex = f.activeIndex;
+
+      switch (args.pointType) {
+        case PointInfoType.mouseMove:
+          f.innerPoints[activeIndex] = args;
+          f.adjustPoints(args);
+          break;
+        case PointInfoType.mouseDown:
+          if (activeIndex == 1) {
+            if (f.xAxisEnd == f.xAxisStart) {
+              break;
+            }
+            f.adjustPoints(args);
+            f.activeIndex = 2;
+          } else if (activeIndex == 2) {
+            doFeatureFinish();
+          }
+          break;
+        default:
+          return false;
+      }
+
+      doCalculate();
+
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    if (needPerfusion) {
+      final point = args.toAreaLogicPoint();
+      feature = EllipsePerfusionImageFeature(this, point);
+    } else {
+      super.handleMouseDownWhileWaiting(args);
+    }
+    state = ItemStates.running;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
+}
+
+class EllipsePerfusionImageFeature extends EllipseFeature {
+  EllipsePerfusionImageFeature(Ellipse refItem, DPoint point)
+      : super(refItem, point);
+
+  ui.Image? perfusionImg;
+  IntRect? perfusionPiexlRect;
+  Rect? perfusionScaleDRect;
+  @override
+  void paint(Canvas canvas, Size size) {
+    if (perfusionImg != null) {
+      Paint paint = Paint();
+
+      Rect src = Rect.fromLTWH(
+        0,
+        0,
+        perfusionImg!.width.toDouble(),
+        perfusionImg!.height.toDouble(),
+      );
+      Rect dst = Rect.fromLTWH(
+        perfusionScaleDRect!.left.toDouble() * size.width,
+        perfusionScaleDRect!.top.toDouble() * size.height,
+        perfusionScaleDRect!.width.toDouble() * size.width,
+        perfusionScaleDRect!.height.toDouble() * size.height,
+      );
+      canvas.drawImageRect(perfusionImg!, src, dst, paint);
+    }
+    super.paint(canvas, size);
+  }
+}

+ 19 - 0
lib/process/primitives/urm_measure/urm_location_vel.dart

@@ -0,0 +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_calcuators/urm_location_vel.dart';
+import 'package:fis_measure/process/primitives/location.dart';
+
+class URMLocationVelMeasure extends Location {
+  URMLocationVelMeasure(super.meta, super.parent);
+
+  static URMLocationVelMeasure createURMLocationVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMLocationVelMeasure measureLocationVel =
+        URMLocationVelMeasure(meta, parent);
+
+    measureLocationVel.calculator = URMLocationVelCal(
+      measureLocationVel,
+    );
+    return measureLocationVel;
+  }
+}

+ 235 - 0
lib/process/primitives/urm_measure/urm_rect_measure.dart

@@ -0,0 +1,235 @@
+import 'dart:ui';
+
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+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/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_den_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_den_vel_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_density.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_fractal_dim.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_hist.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_perfusion.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_rect_vel_measure.dart';
+import 'package:fis_measure/process/items/item.dart';
+import 'package:fis_measure/utils/canvas.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+import 'dart:ui' as ui;
+
+import '../../items/item_feature.dart';
+
+class URMRectMeasure extends MeasureItem<URMRectFeature> {
+  URMRectMeasure(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
+  static bool needPerfusion = false;
+
+  static URMRectMeasure createMeasureRect(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    // measureRect.calculator = URMRectCal(measureRect, );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectDensity(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectDensityCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectFractalDim(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectFractalDimCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectDenMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectDenMeasureCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectPerfusion(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    needPerfusion = true;
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectPerfusionCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectHist(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectHistCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectDenVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRect = URMRectMeasure(meta, parent);
+    measureRect.calculator = URMRectDenVelMeasureCal(
+      measureRect,
+    );
+    return measureRect;
+  }
+
+  static URMRectMeasure createURMRectVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMRectMeasure measureRectVel = URMRectMeasure(meta, parent);
+    measureRectVel.calculator = URMRectVelMeasureCal(
+      measureRectVel,
+    );
+    return measureRectVel;
+  }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (waitingResult) return false;
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      }
+    }
+
+    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;
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleFinish();
+      }
+    }
+    return true;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
+
+  @override
+  bool onExecuteTouch(PointInfo args) {
+    // TODO: implement onExecuteTouch
+    throw UnimplementedError();
+  }
+
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    // TODO: 判断是否当前area
+    // 转换为Area逻辑位置
+    final point = args.toAreaLogicPoint();
+    if (needPerfusion) {
+      feature = RectPerfusionImageFeature(this, point, point);
+      if (args.hostVisualArea != null) {
+        feature!.hostVisualArea = args.hostVisualArea;
+      }
+    } else {
+      feature = URMRectFeature(this, point, point);
+      if (args.hostVisualArea != null) {
+        feature!.hostVisualArea = args.hostVisualArea;
+      }
+    }
+
+    state = ItemStates.running;
+  }
+}
+
+class URMRectFeature extends MeasureItemFeature {
+  URMRectFeature(
+    IMeasureItem refItem,
+    DPoint startPoint,
+    DPoint endPoint,
+  ) : super(refItem) {
+    innerPoints.add(startPoint);
+    innerPoints.add(endPoint);
+  }
+
+  /// 起点
+  DPoint get startPoint => innerPoints[0];
+  set startPoint(DPoint value) => innerPoints[0] = value;
+
+  /// 终点
+  DPoint get endPoint => innerPoints[1];
+  set endPoint(DPoint value) => innerPoints[1] = value;
+
+  List<List<Offset>> offsetsList = [];
+  // List<List<Offset>> scaledOffsetsList = [];
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    if (startPoint == endPoint) return;
+    drawId(canvas, size, idText);
+
+    final startOffset = convert2ViewPoint(size, startPoint).toOffset();
+    drawVertex(canvas, startOffset);
+
+    final endOffset = convert2ViewPoint(size, endPoint).toOffset();
+    canvas.drawDashRect(startOffset, endOffset, 1, 10, paintPan);
+    drawVertex(canvas, endOffset, isActive);
+    //绘制点集连线
+    // scaledOffsetsList = offsetsList.map((e) {
+    //   return e.map((e) {
+    //     return e * refItem.scaleRatio;
+    //   }).toList();
+    // }).toList();
+    for (var offsets in offsetsList) {
+      canvas.drawPointsLine(offsets, paintLinePan);
+    }
+  }
+}
+
+class RectPerfusionImageFeature extends URMRectFeature {
+  RectPerfusionImageFeature(
+    IMeasureItem refItem,
+    DPoint startPoint,
+    DPoint endPoint,
+  ) : super(refItem, startPoint, endPoint);
+
+  ui.Image? perfusionImg;
+  IntRect? perfusionPiexlRect;
+  Rect? perfusionScaleDRect;
+  @override
+  void paint(Canvas canvas, Size size) {
+    if (perfusionImg != null) {
+      Paint paint = Paint();
+
+      Rect src = Rect.fromLTWH(
+        0,
+        0,
+        perfusionImg!.width.toDouble(),
+        perfusionImg!.height.toDouble(),
+      );
+      Rect dst = Rect.fromLTWH(
+        perfusionScaleDRect!.left.toDouble() * size.width,
+        perfusionScaleDRect!.top.toDouble() * size.height,
+        perfusionScaleDRect!.width.toDouble() * size.width,
+        perfusionScaleDRect!.height.toDouble() * size.height,
+      );
+      canvas.drawImageRect(perfusionImg!, src, dst, paint);
+    }
+    super.paint(canvas, size);
+  }
+}

+ 77 - 0
lib/process/primitives/urm_measure/urm_shell_measure.dart

@@ -0,0 +1,77 @@
+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_shell_den_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_den_vel_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_density_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_fractal_dim_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_hist_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_perfusion_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_shell_vel_measure.dart';
+import 'package:fis_measure/process/primitives/trace.dart';
+
+class URMShellMeasure extends Trace {
+  URMShellMeasure(super.meta, super.parent);
+
+  static URMShellMeasure createURMShellDenMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellDen = URMShellMeasure(meta, parent);
+    measureShellDen.calculator = URMShellDenMeasureCal(
+      measureShellDen,
+    );
+    return measureShellDen;
+  }
+
+  static URMShellMeasure createURMShellVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellVel = URMShellMeasure(meta, parent);
+    measureShellVel.calculator = URMShellVelMeasureCal(
+      measureShellVel,
+    );
+    return measureShellVel;
+  }
+
+  static URMShellMeasure createURMShellDensityMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellDensity = URMShellMeasure(meta, parent);
+    measureShellDensity.calculator = URMShellDensityMeasureCal(
+      measureShellDensity,
+    );
+    return measureShellDensity;
+  }
+
+  static URMShellMeasure createURMShellFractalDimMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellFractalDim = URMShellMeasure(meta, parent);
+    measureShellFractalDim.calculator = URMShellFractalDimMeasureCal(
+      measureShellFractalDim,
+    );
+    return measureShellFractalDim;
+  }
+
+  static URMShellMeasure createURMShellPerfusionMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellPerfusion = URMShellMeasure(meta, parent);
+    measureShellPerfusion.calculator = URMShellPerfusionMeasureCal(
+      measureShellPerfusion,
+    );
+    return measureShellPerfusion;
+  }
+
+  static URMShellMeasure createURMShellHistMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellHist = URMShellMeasure(meta, parent);
+    measureShellHist.calculator = URMShellHistMeasureCal(
+      measureShellHist,
+    );
+    return measureShellHist;
+  }
+
+  static URMShellMeasure createURMShellDenVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMShellMeasure measureShellDenVel = URMShellMeasure(meta, parent);
+    measureShellDenVel.calculator = URMShellDenVelMeasureCal(
+      measureShellDenVel,
+    );
+    return measureShellDenVel;
+  }
+}

+ 14 - 24
lib/process/primitives/urm_straightline.dart → lib/process/primitives/urm_measure/urm_straight_curvature_line_measure.dart

@@ -3,21 +3,23 @@ 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/interfaces/process/workspace/point_info.dart';
-import 'package:fis_measure/process/calcuators/urm.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_straight_curvature_line.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 import 'package:fis_measure/utils/canvas.dart';
 import 'package:fis_measure/utils/prompt_box.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/painting.dart';
 
-class URMStraightLine extends StraightLine {
-  URMStraightLine(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
+class URMStraightCurvatureLineMeasure extends StraightLine {
+  URMStraightCurvatureLineMeasure(ItemMeta meta, IMeasureItem? parent)
+      : super(meta, parent);
 
-  static URMStraightLine createURMStraightline(ItemMeta meta,
+  static URMStraightCurvatureLineMeasure createURMStraightCurvatureLineMeasure(
+      ItemMeta meta,
       [IMeasureItem? parent]) {
-    URMStraightLine sraightLine = URMStraightLine(meta, parent);
+    URMStraightCurvatureLineMeasure sraightLine =
+        URMStraightCurvatureLineMeasure(meta, parent);
     sraightLine.repeatableEditable = true;
-    sraightLine.calculator = URMAutoLineLineCal(sraightLine, type: meta.name);
+    sraightLine.calculator = URMStraightCurvatureLineCal(sraightLine);
     return sraightLine;
   }
 
@@ -53,7 +55,7 @@ class URMStraightLine extends StraightLine {
   @override
   void handleMouseDownWhileWaiting(PointInfo args) {
     final point = args.toAreaLogicPoint();
-    feature = URMAutoLineFeature(this, point, point);
+    feature = StraightCurvatureFeature(this, point, point);
     if (args.hostVisualArea != null) {
       // handleTissueTM(args.hostVisualArea!.mode.modeType, point);
       feature!.hostVisualArea = args.hostVisualArea;
@@ -74,28 +76,16 @@ class URMStraightLine extends StraightLine {
   }
 }
 
-class URMAutoLineFeature extends StraightLineFeature {
-  URMAutoLineFeature(IMeasureItem refItem, DPoint startPoint, DPoint endPoint)
+class StraightCurvatureFeature extends StraightLineFeature {
+  StraightCurvatureFeature(
+      IMeasureItem refItem, DPoint startPoint, DPoint endPoint)
       : super(refItem, startPoint, endPoint);
 
   List<DPoint> autoLinePoints = [];
 
   @override
   void paint(Canvas canvas, Size size) {
-    if (startPoint == endPoint) return;
-    drawId(canvas, size, idText);
-
-    final startOffset = convert2ViewPoint(size, startPoint).toOffset();
-    final endOffset = convert2ViewPoint(size, endPoint).toOffset();
-    final distanceOffset = endOffset - startOffset;
-    if (distanceOffset.distance < measureData.getMinCursorDistance()) {
-      drawMiniVertex(canvas, startOffset);
-      drawMiniVertex(canvas, endOffset, isActive);
-    } else {
-      drawVertex(canvas, startOffset);
-      drawVertex(canvas, endOffset, isActive);
-    }
-    canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan);
+    super.paint(canvas, size);
 
     /// 绘制 AutoLine 点集连线
     /// 使用 convert2ViewPoint 转为像素坐标

+ 212 - 0
lib/process/primitives/urm_measure/urm_trace_measure.dart

@@ -0,0 +1,212 @@
+import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/date_types/point.dart';
+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/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_den_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_den_vel_measure.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_density.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_fractal_dim.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_hist.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_perfusion.dart';
+import 'package:fis_measure/process/calcuators/urm_calcuators/urm_trace_vel_measure.dart';
+import 'package:fis_measure/process/primitives/area_abstract.dart';
+import 'package:fis_measure/process/primitives/trace.dart';
+import 'package:fis_measure/utils/prompt_box.dart';
+import 'package:flutter/material.dart';
+import 'dart:ui' as ui;
+
+import 'package:flutter/services.dart';
+
+class URMTraceMeasure extends Trace {
+  URMTraceMeasure(super.meta, super.parent);
+  static bool needPerfusion = false;
+
+  static URMTraceMeasure createURMTraceDensity(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceDensity = URMTraceMeasure(meta, parent);
+    measureTraceDensity.calculator = URMTraceDensityCal(
+      measureTraceDensity,
+    );
+    return measureTraceDensity;
+  }
+
+  static URMTraceMeasure createURMTraceFractalDim(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceFractalDim = URMTraceMeasure(meta, parent);
+    measureTraceFractalDim.calculator = URMTraceFractalDimCal(
+      measureTraceFractalDim,
+    );
+    return measureTraceFractalDim;
+  }
+
+  static URMTraceMeasure createURMTraceDenMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceDen = URMTraceMeasure(meta, parent);
+    measureTraceDen.calculator = URMTraceDenMeasureCal(
+      measureTraceDen,
+    );
+    return measureTraceDen;
+  }
+
+  static URMTraceMeasure createURMTraceVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceVel = URMTraceMeasure(meta, parent);
+    measureTraceVel.calculator = URMTraceVelMeasureCal(
+      measureTraceVel,
+    );
+
+    return measureTraceVel;
+  }
+
+  static URMTraceMeasure createURMTracePerfusion(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTracePerfusion = URMTraceMeasure(meta, parent);
+    measureTracePerfusion.calculator = URMTracePerfusionCal(
+      measureTracePerfusion,
+    );
+    needPerfusion = true;
+    return measureTracePerfusion;
+  }
+
+  static URMTraceMeasure createURMTraceHist(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceHist = URMTraceMeasure(meta, parent);
+    measureTraceHist.calculator = URMTraceHistCal(
+      measureTraceHist,
+    );
+    return measureTraceHist;
+  }
+
+  static URMTraceMeasure createURMTraceDenVelMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMTraceMeasure measureTraceDenVel = URMTraceMeasure(meta, parent);
+    measureTraceDenVel.calculator = URMTraceDenVelMeasureCal(
+      measureTraceDenVel,
+    );
+
+    return measureTraceDenVel;
+  }
+
+  @override
+  bool onExecuteMouse(PointInfo args) {
+    if (waitingResult) return false;
+    if (state == ItemStates.finished) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        state = ItemStates.waiting;
+      }
+    }
+
+    if (state == ItemStates.waiting) {
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleMouseDownWhileWaiting(args);
+      }
+    } else if (state == ItemStates.running) {
+      if (args.pointType == PointInfoType.mouseUp) return false;
+
+      feature?.adopt(args);
+      if (args.pointType == PointInfoType.mouseDown) {
+        handleFinish();
+      } else {
+        checkAutoSnap(args);
+      }
+    }
+    return true;
+  }
+
+  @override
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    if (needPerfusion) {
+      feature = TracePerfusionImageFeature(this);
+    } else {
+      super.handleMouseDownWhileWaiting(args);
+    }
+    state = ItemStates.running;
+  }
+
+  bool waitingResult = false;
+
+  void handleFinish() async {
+    feature!.isActive = false;
+    PromptBox.loading("计算中...");
+    waitingResult = true;
+    await doCalculateAsync();
+    doFeatureFinish();
+    PromptBox.dismiss();
+    waitingResult = false;
+  }
+
+  /// 自动结束检测
+  @override
+  bool checkAutoSnap(PointInfo current, [bool autoFinishFeature = true]) {
+    if (feature == null || feature!.innerPoints.length < 3) {
+      isSmartMove = false;
+      return _syncState(false);
+    }
+
+    // final viewport = feature!.hostVisualArea!.viewport!;
+
+    if (isAutoSnap == false) return false;
+    final pixelSize = application.displaySize;
+
+    final p1 = feature!.innerPoints.first.scale2Size(pixelSize);
+    final p2 = current.scale2Size(pixelSize);
+    final length = (p1 - p2).length;
+    if (length > snapThreshold * 2.0 && !isSmartMove) {
+      isSmartMove = true;
+    }
+    if (length < snapThreshold && isSmartMove) {
+      feature!.innerPoints.last = feature!.innerPoints.first.clone();
+
+      /// 此处的最后一个点,方便计算,但是绘制时要剔除
+      // feature!.innerPoints.removeLast();
+
+      HapticFeedback.heavyImpact();
+      if (autoFinishFeature) {
+        handleFinish();
+      }
+      isSmartMove = false;
+      return _syncState(true);
+    } else {
+      return _syncState(false);
+    }
+  }
+
+  bool _syncState(bool isSnap) {
+    snapState = isSnap;
+    return snapState;
+  }
+}
+
+class TracePerfusionImageFeature extends TraceFeature {
+  TracePerfusionImageFeature(AreaItemAbstract refItem) : super(refItem);
+
+  ui.Image? perfusionImg;
+  IntRect? perfusionPiexlRect;
+  DPoint? leftTopPoint;
+  DPoint? rightBottomPoint;
+  @override
+  void paint(Canvas canvas, Size size) {
+    if (perfusionImg != null) {
+      Paint paint = Paint();
+
+      Rect src = Rect.fromLTWH(
+        0,
+        0,
+        perfusionImg!.width.toDouble(),
+        perfusionImg!.height.toDouble(),
+      );
+      if (leftTopPoint == null || rightBottomPoint == null) {
+        return;
+      }
+      DPoint leftTop = convert2ViewPoint(size, leftTopPoint!);
+      DPoint rightBottom = convert2ViewPoint(size, rightBottomPoint!);
+      convert2ViewPoint(size, rightBottomPoint!);
+      Rect dst = Rect.fromPoints(
+          Offset(leftTop.x, leftTop.y), Offset(rightBottom.x, rightBottom.y));
+      canvas.drawImageRect(perfusionImg!, src, dst, paint);
+    }
+    super.paint(canvas, size);
+  }
+}

+ 17 - 2
lib/process/primitives/combos/urm_vel.dart → lib/process/primitives/urm_measure/urm_vel.dart

@@ -1,10 +1,13 @@
 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/combos/urm_vel_combo.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';
 
 class URMVel extends URMVelAbstract<URMVelFeature> {
   static const String _area1Key = "SRLoactionVel";
@@ -42,7 +45,7 @@ class URMVel extends URMVelAbstract<URMVelFeature> {
   @override
   void onCancelingOnce() {}
 
-  static URMVel crateURMVel(ItemMeta meta, [IMeasureItem? parent]) {
+  static URMVel create(ItemMeta meta, [IMeasureItem? parent]) {
     var urmVel = URMVel(meta);
     urmVel.calculator = URMVelCal(urmVel);
 
@@ -55,3 +58,15 @@ class URMVelFeature extends TopMeasureItemFeature {
     ITopMeasureItem refItem,
   ) : super(refItem);
 }
+
+abstract class URMVelAbstract<T extends MeasureItemFeature>
+    extends TopMeasureItem<T> {
+  URMVelAbstract(ItemMeta meta) : super(meta);
+
+  MeasureItem get child1;
+  MeasureItem get child2;
+  MeasureItem get child3;
+
+  @override
+  bool get finishAfterUnactive => true;
+}

+ 15 - 0
lib/process/primitives/urm_measure/urm_vessel_measure.dart

@@ -0,0 +1,15 @@
+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';
+
+class URMVesselMeasure extends StraightLine {
+  URMVesselMeasure(super.meta, super.parent);
+
+  static URMVesselMeasure createURMVesselMeasure(ItemMeta meta,
+      [IMeasureItem? parent]) {
+    URMVesselMeasure measureVessel = URMVesselMeasure(meta, parent);
+    measureVessel.calculator = DistanceCal(measureVessel);
+    return measureVessel;
+  }
+}

+ 0 - 120
lib/process/primitives/urm_rect.dart

@@ -1,120 +0,0 @@
-import 'dart:ui';
-
-import 'package:fis_measure/interfaces/date_types/point.dart';
-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/interfaces/process/workspace/point_info.dart';
-import 'package:fis_measure/process/calcuators/urm.dart';
-import 'package:fis_measure/process/items/item.dart';
-import 'package:fis_measure/utils/canvas.dart';
-import 'package:fis_measure/utils/prompt_box.dart';
-
-import '../items/item_feature.dart';
-
-class URMRect extends MeasureItem<URMRectFeature> {
-  URMRect(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
-
-  static URMRect createMeasureRect(ItemMeta meta, [IMeasureItem? parent]) {
-    URMRect measureRect = URMRect(meta, parent);
-    measureRect.calculator = URMRectCal(measureRect, type: meta.name);
-    return measureRect;
-  }
-
-  @override
-  bool onExecuteMouse(PointInfo args) {
-    if (waitingResult) return false;
-    if (state == ItemStates.finished) {
-      if (args.pointType == PointInfoType.mouseDown) {
-        state = ItemStates.waiting;
-      }
-    }
-
-    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;
-      if (args.pointType == PointInfoType.mouseDown) {
-        handleFinish();
-      }
-    }
-    return true;
-  }
-
-  bool waitingResult = false;
-
-  void handleFinish() async {
-    feature!.isActive = false;
-    PromptBox.loading("计算中...");
-    waitingResult = true;
-    await doCalculateAsync();
-    doFeatureFinish();
-    PromptBox.dismiss();
-    waitingResult = false;
-  }
-
-  @override
-  bool onExecuteTouch(PointInfo args) {
-    // TODO: implement onExecuteTouch
-    throw UnimplementedError();
-  }
-
-  void handleMouseDownWhileWaiting(PointInfo args) {
-    // TODO: 判断是否当前area
-    // 转换为Area逻辑位置
-    final point = args.toAreaLogicPoint();
-    feature = URMRectFeature(this, point, point);
-    if (args.hostVisualArea != null) {
-      feature!.hostVisualArea = args.hostVisualArea;
-    }
-    state = ItemStates.running;
-  }
-}
-
-class URMRectFeature extends MeasureItemFeature {
-  URMRectFeature(
-    IMeasureItem refItem,
-    DPoint startPoint,
-    DPoint endPoint,
-  ) : super(refItem) {
-    innerPoints.add(startPoint);
-    innerPoints.add(endPoint);
-  }
-
-  /// 起点
-  DPoint get startPoint => innerPoints[0];
-  set startPoint(DPoint value) => innerPoints[0] = value;
-
-  /// 终点
-  DPoint get endPoint => innerPoints[1];
-  set endPoint(DPoint value) => innerPoints[1] = value;
-
-  List<List<Offset>> offsetsList = [];
-  // List<List<Offset>> scaledOffsetsList = [];
-
-  @override
-  void paint(Canvas canvas, Size size) {
-    if (startPoint == endPoint) return;
-    drawId(canvas, size, idText);
-
-    final startOffset = convert2ViewPoint(size, startPoint).toOffset();
-    drawVertex(canvas, startOffset);
-
-    final endOffset = convert2ViewPoint(size, endPoint).toOffset();
-    canvas.drawDashRect(startOffset, endOffset, 1, 10, paintPan);
-    drawVertex(canvas, endOffset, isActive);
-    //绘制点集连线
-    // scaledOffsetsList = offsetsList.map((e) {
-    //   return e.map((e) {
-    //     return e * refItem.scaleRatio;
-    //   }).toList();
-    // }).toList();
-    for (var offsets in offsetsList) {
-      canvas.drawPointsLine(offsets, paintLinePan);
-    }
-  }
-}

+ 1 - 1
lib/process/primitives/urm_trace.dart

@@ -11,7 +11,7 @@ class URMTrace extends Trace {
 
   static URMTrace createURMTrace(ItemMeta meta, [IMeasureItem? parent]) {
     URMTrace trace = URMTrace(meta, parent);
-    trace.calculator = URMTraceCal(trace, type: meta.name);
+    // trace.calculator = URMTraceCal(trace, type: meta.name);
     trace.isClosed = false;
     return trace;
   }

+ 67 - 233
lib/process/workspace/urm/application.dart

@@ -3,17 +3,10 @@ import 'package:fis_common/logger/logger.dart';
 import 'package:fis_jsonrpc/services/aIDiagnosis.m.dart';
 import 'package:fis_measure/interfaces/date_types/point.dart';
 import 'package:fis_measure/interfaces/process/urm/urm_data_processor.dart';
-import 'package:fis_measure/interfaces/process/visuals/visual.dart';
-import 'package:fis_measure/process/visual/v2d_visual.dart';
 import 'package:fis_measure/process/workspace/rpc_bridge.dart';
 import 'package:flutter/material.dart';
 import 'package:vid/us/vid_us_probe.dart';
 import '../application.dart';
-import 'package:fis_measure/interfaces/date_types/rect_region.dart';
-import 'package:vid/us/vid_us_2d_visual.dart';
-import 'package:vid/us/vid_us_logical_coordinate.dart';
-import 'package:vid/us/vid_us_physical_coordinate.dart';
-import 'package:vid/us/vid_us_visual.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
 
 /// URM 专业应用
@@ -36,12 +29,15 @@ class URMApplication extends Application {
 
   @override
   PointInfo createPointInfo(Offset offset, PointInfoType type) {
-    // 当前获取到的 displaysize 是错误的,需要手动转一道,将 offset 转为正确的屏幕百分比坐标
-    final width = displaySize.width;
+    final width = urmDataProcessor.showLeftRight
+        ? displaySize.width / 2
+        : displaySize.width;
     final height = displaySize.height;
     final x = offset.dx / width;
     final y = offset.dy / height;
-    final percentOffset = Offset(x, y);
+    Rect zoomArea = urmDataProcessor.measureAreaInFullVisual;
+    Offset percentOffset = Offset(x, y);
+    percentOffset = pointPctOutRect(zoomArea, percentOffset);
     final info = PointInfo.fromOffset(percentOffset, type);
     info.hostVisualArea = currentVisualArea; // 未切换区域则沿用当前区域
 
@@ -92,7 +88,7 @@ class URMApplication extends Application {
           srcDPoints: srcDPoints, // 手动传入
           cMlength: cMlength, // 手动传入
           shellWidth: shellWidth, // 手动传入
-          phywidth: 4.09846, // TODO 手动计算
+          phywidth: urmDataProcessor.phywidth, // 手动计算
           urmImageType: params.urmImageType, // 手动取值
           urmBlend: params.urmBlend, // 手动取值
           downsampleIndex: params.downsampleIndex, // 手动取值
@@ -128,238 +124,60 @@ class URMApplication extends Application {
     }
   }
 
+  Offset pointPctOutRect(Rect r, Offset p) {
+    double xPctInR = p.dx * r.width + r.left;
+    double yPctInR = p.dy * r.height + r.top;
+    return Offset(xPctInR, yPctInR);
+  }
+
+  Offset pointPctInRect(Rect r, Offset p) {
+    double xPctOutR = (p.dx - r.left) / r.width;
+    double yPctOutR = (p.dy - r.top) / r.height;
+    return Offset(xPctOutR, yPctOutR);
+  }
+
   // 本地完整图像内归一化坐标转视图(接口中 screenWidth、screenHeight 指代区域)内归一化坐标
   UrmPoint localToView(DPoint point) {
-    return UrmPoint(
-      x: point.x * resultWidth,
-      y: point.y * resultHeight,
-    );
+    Rect urmScreen = urmDataProcessor.urmScreenInFullVisual;
+    Offset percentOffset = Offset(point.x, point.y);
+    percentOffset = pointPctInRect(urmScreen, percentOffset);
+    return UrmPoint(x: percentOffset.dx, y: percentOffset.dy);
   }
 
   // 视图(接口中 screenWidth、screenHeight 指代区域)内归一化坐标转本地完整图像内归一化坐标
   DPoint viewToLocal(UrmPoint point) {
-    return DPoint(
-      point.x / resultWidth,
-      point.y / resultHeight,
-    );
+    Rect urmScreen = urmDataProcessor.urmScreenInFullVisual;
+    Offset percentOffset = Offset(point.x, point.y);
+    percentOffset = pointPctOutRect(urmScreen, percentOffset);
+    return DPoint(percentOffset.dx, percentOffset.dy);
   }
 
-  // Future<GetSRRoiVelResult?> getSRRoiVelResult(
-  //     DPoint startPoint, DPoint endPoint) async {
-  //   try {
-  //     print("调接口获取测量值 getSRRoiVelAsync");
-  //     GetSRRoiVelResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRRoiVelAsync(
-  //       GetSRRoiVelRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 5,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetSRRoiFractalDimResult?> getSRRoiFractalDimResult(
-  //     DPoint startPoint, DPoint endPoint) async {
-  //   try {
-  //     print("调接口获取测量值 getSRRoiFractalDimAsync");
-  //     GetSRRoiFractalDimResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRRoiFractalDimAsync(
-  //       GetSRRoiFractalDimRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 5,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetSRTraceVelResult?> getSRTraceVelResult(
-  //   List<UrmPoint> points,
-  // ) async {
-  //   try {
-  //     print("调接口获取测量值 getSRTraceVelResult");
-  //     GetSRTraceVelResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRTraceVelAsync(
-  //       GetSRTraceVelRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 5,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         inputPoints: points,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetSRTraceFractalDimResult?> getSRTraceFractalDimResult(
-  //   List<UrmPoint> points,
-  // ) async {
-  //   try {
-  //     print("调接口获取测量值 getSRTraceFractalDimAsync");
-  //     GetSRTraceFractalDimResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRTraceFractalDimAsync(
-  //       GetSRTraceFractalDimRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 5,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         inputPoints: points,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetSRLoactionVelResult?> getSRLoactionVelResult(
-  //   DPoint point,
-  // ) async {
-  //   try {
-  //     print("调接口获取测量值 getSRLoactionVelAsync");
-  //     GetSRLoactionVelResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRLoactionVelAsync(
-  //       GetSRLoactionVelRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 6,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: point.x,
-  //         startPointY: point.y,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetSRRoiSpeedResult?> getSRRoiSpeedResult(
-  //   DPoint startPoint,
-  //   DPoint endPoint,
-  // ) async {
-  //   try {
-  //     print("调接口获取测量值 getSRRoiSpeedAsync");
-  //     GetSRRoiSpeedResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getSRRoiSpeedAsync(
-  //       GetSRRoiSpeedRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 6,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
-
-  // Future<GetURMDenMeasureResult?> getURMDenMeasureResult(
-  //     DPoint startPoint, DPoint endPoint) async {
-  //   try {
-  //     print("调接口获取测量值 getURMDenMeasureAsync");
-  //     GetURMDenMeasureResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getURMDenMeasureAsync(
-  //       GetURMDenMeasureRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 5,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         areaScaler: areaScaler,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
+  // 视图(接口中 screenWidth、screenHeight 指代区域)内归一化矩形转本地完整图像内归一化矩形
+  Rect viewToLocalRect(DRect point) {
+    DPoint p1 = viewToLocal(UrmPoint(x: point.left, y: point.top));
+    DPoint p2 = viewToLocal(UrmPoint(x: point.right, y: point.bottom));
+    return Rect.fromLTRB(p1.x, p1.y, p2.x, p2.y);
+  }
 
-  // Future<GetURMVelMeasureResult?> getURMVelMeasureResult(
-  //     DPoint startPoint, DPoint endPoint) async {
-  //   try {
-  //     print("调接口获取测量值 getURMVelMeasureAsync");
-  //     GetURMVelMeasureResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getURMVelMeasureAsync(
-  //       GetURMVelMeasureRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 6,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         areaScaler: areaScaler,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
+  // 类型转换
+  List<UrmPoint> dPointsToUrmPoints(List<DPoint>? points) {
+    if (points == null) return [];
+    List<UrmPoint> urmPoints = [];
+    for (var point in points) {
+      urmPoints.add(localToView(point));
+    }
+    return urmPoints;
+  }
 
-  // Future<GetUrmVessMeasureResult?> getURMVessMeasureResult(
-  //     DPoint startPoint, DPoint endPoint, double cmlength) async {
-  //   try {
-  //     print("调接口获取测量值 getURMVelMeasureAsync");
-  //     GetUrmVessMeasureResult result =
-  //         await RPCBridge.ins.rpc.aIDiagnosis.getUrmVessMeasureAsync(
-  //       GetUrmVessMeasureRequest(
-  //         remedicalCode: remedicalCode,
-  //         measureMode: 6,
-  //         dataWidth: resultWidth,
-  //         dataHeight: resultHeight,
-  //         startPointX: startPoint.x,
-  //         startPointY: startPoint.y,
-  //         endPointX: endPoint.x,
-  //         endPointY: endPoint.y,
-  //         areaScaler: pixelscaler,
-  //         cmLength: cmlength,
-  //         token: RPCBridge.ins.userToken,
-  //       ),
-  //     );
-  //     return result;
-  //   } catch (e) {
-  //     return null;
-  //   }
-  // }
+  // 类型转换
+  List<DPoint> urmPointsToDPoints(List<UrmPoint>? points) {
+    if (points == null) return [];
+    List<DPoint> urmPoints = [];
+    for (var point in points) {
+      urmPoints.add(viewToLocal(point));
+    }
+    return urmPoints;
+  }
 }
 
 class URMChartParams {
@@ -375,3 +193,19 @@ class URMChartParams {
     required this.minPointIndex,
   });
 }
+
+class URMSimpleMeasureParams {
+  final URMMeasureType urmMeasureType;
+  final URMROIType rOIType;
+  final List<UrmPoint>? srcDPoints;
+  final double? cMlength;
+  double? shellWidth = 0;
+
+  URMSimpleMeasureParams({
+    required this.urmMeasureType,
+    required this.rOIType,
+    this.srcDPoints,
+    this.cMlength,
+    this.shellWidth,
+  });
+}

+ 1 - 1
pubspec.yaml

@@ -100,7 +100,7 @@ dependency_overrides:
   fis_jsonrpc:
     git:
       url: http://git.ius.plus:88/Project-Wing/fis_lib_jsonrpc.git
-      ref: "9ecd100"
+      ref: "cf62b49"
   fis_lib_business_components:
     git:
       url: http://git.ius.plus/Project-Wing/fis_lib_business_components.git