Browse Source

体积计算

melon.yin 2 years ago
parent
commit
d877b7ab96

+ 12 - 3
lib/interfaces/process/items/item_metas.dart

@@ -12,7 +12,7 @@ class ItemMeta {
   String briefAnnotation;
 
   /// 测量类型
-  String measureType;
+  String? measureType;
 
   /// 组合测量方法
   String? multiMethod;
@@ -24,14 +24,23 @@ class ItemMeta {
   List<ItemMeta> childItems;
 
   ItemMeta(
-    this.name,
-    this.measureType, {
+    this.name, {
+    this.measureType,
     required this.description,
     required this.outputs,
     this.briefAnnotation = '',
     this.multiMethod,
     this.childItems = const [],
   });
+
+  /// 根据名称获取子项
+  ItemMeta? getChildByName(String name) {
+    final matchList = childItems.where((e) => e.name == name);
+    if (matchList.isNotEmpty) {
+      return matchList.first;
+    }
+    return null;
+  }
 }
 
 class ItemOutputMeta {

+ 36 - 7
lib/measure_page_test.dart

@@ -61,13 +61,13 @@ class MeasureTestPage extends StatefulWidget {
 class _MeasureTestPageState extends State<MeasureTestPage> {
   static const C_LINEAR_TISSUE =
       // "http://cdn-bj.fis.plus/9F066341FA874E21B48CDE247C13D495.vid"; //B TVI TD
-      "http://cdn-bj.fis.plus/974BABA5113640639FD749E06DD7DA5B.vid"; //B CF CW
-  // "http://cdn-bj.fis.plus/0B344F48BA574ECD82B7FEDB8848421A.vid";//单幅TM
-  // "http://cdn-bj.fis.plus/F26C6E5D57A7472A97E9EB543DF0D16C.vid"; // 单幅Convex
-  // "http://cdn-bj.fis.plus/6B6E069659D14E7299EB9F6EFCDE9C8C.vid"; //双幅单TissueConvex
-  // "http://cdn-bj.fis.plus/062643B82365437DB95F3811580AF3ED.vid"; //四幅单模式
-  // "http://cdn-bj.fis.plus/EA90D146049D416E8E466B7446E00001.vid"; //四幅Doppler
-  // "http://cdn-bj.fis.plus/3rd_linearTvTissue2.vid"; //魔盒
+      // "http://cdn-bj.fis.plus/974BABA5113640639FD749E06DD7DA5B.vid"; //B CF CW
+      // "http://cdn-bj.fis.plus/0B344F48BA574ECD82B7FEDB8848421A.vid";//单幅TM
+      // "http://cdn-bj.fis.plus/F26C6E5D57A7472A97E9EB543DF0D16C.vid"; // 单幅Convex
+      // "http://cdn-bj.fis.plus/6B6E069659D14E7299EB9F6EFCDE9C8C.vid"; //双幅单TissueConvex
+      // "http://cdn-bj.fis.plus/062643B82365437DB95F3811580AF3ED.vid"; //四幅单模式
+      // "http://cdn-bj.fis.plus/EA90D146049D416E8E466B7446E00001.vid"; //四幅Doppler
+      "http://cdn-bj.fis.plus/3rd_linearTvTissue2.vid"; //魔盒
   // "http://cdn-bj.fis.plus/81FFF8E5E078473FA687FBE81C4869B1.vid"; // 魔盒TV
   // "http://cdn-bj.fis.plus/7B450708A2784B1490304C82787349BE.vid";// 胎儿Zoom
   static const C_CONVEX_TISSUE =
@@ -305,6 +305,7 @@ class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
     MeasureTypes.Perimeter,
     MeasureTypes.Area,
     MeasureTypes.Depth,
+    MeasureTypes.Volume,
   ];
 
   final scrollController = ScrollController();
@@ -369,6 +370,34 @@ class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
     );
   }
 
+  void buildItems() {
+    final count = C_SUPPORTED_ITEMS.length;
+    final children = <Widget>[];
+    for (var i = 0; i < count; i++) {
+      final name = C_SUPPORTED_ITEMS[i];
+      final active = i == activeIndex;
+      final widget = active
+          ? ElevatedButton(
+              onPressed: () => changeItem(i),
+              child: Text(name),
+              style: ElevatedButton.styleFrom(
+                fixedSize: const Size.fromHeight(50),
+              ),
+            )
+          : OutlinedButton(
+              onPressed: () => changeItem(i),
+              child: Text(name),
+              style: OutlinedButton.styleFrom(
+                fixedSize: const Size.fromHeight(50),
+              ),
+            );
+      if (i > 0) {
+        children.add(const SizedBox(height: 8));
+      }
+      children.add(widget);
+    }
+  }
+
   void changeItem(int index) {
     setState(() {
       activeIndex = index;

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

@@ -0,0 +1,32 @@
+// ignore_for_file:  constant_identifier_names
+
+import 'dart:math' as math;
+
+class GeneralFormulas {
+  /// Volume:1/6 x π
+  static const double VolumeCofficient = math.pi / 6.0;
+
+  /// 带比率系数算体积
+  ///
+  /// [coefficient] 比率系数
+  static double volumeWithCoefficient(
+    double d1,
+    double d2,
+    double d3,
+    double coefficient,
+  ) {
+    double volume = 0;
+    if (!doubleAlmostEquals(d1, 0) &&
+        !doubleAlmostEquals(d2, 0) &&
+        !doubleAlmostEquals(d3, 0)) {
+      volume = d1 * d2 * d3 * coefficient;
+    }
+    return volume;
+  }
+
+  static bool doubleAlmostEquals(double num1, double num2,
+      [double precision = 0.000001]) {
+    if (num1.isNaN && num2.isNaN) return true;
+    return (num1 - num2).abs() <= precision;
+  }
+}

+ 48 - 0
lib/process/calcuators/volume.dart

@@ -0,0 +1,48 @@
+import 'package:fis_measure/interfaces/process/calculators/output.dart';
+import 'package:fis_measure/process/calcuators/formulas/general.dart';
+import 'package:fis_measure/process/primitives/combos/lwh_straightline.dart';
+import 'package:fis_measure/process/primitives/straightline.dart';
+
+import 'calculator.dart';
+
+class VolumeThreeDistanceCal extends Calculator<LWHStraightLine, double> {
+  VolumeThreeDistanceCal(LWHStraightLine ref) : super(ref);
+
+  @override
+  void calculate() {
+    if (ref.feature == null) return;
+
+    final outputMeta = ref.meta.outputs.first;
+    final outputItem = createOutput(outputMeta.name, 0, outputMeta.unit);
+    final l = _pickChildOutput(ref.l);
+    final w = _pickChildOutput(ref.w);
+    final h = _pickChildOutput(ref.h);
+
+    String description;
+    if (l != null && w != null && h != null) {
+      final value = GeneralFormulas.volumeWithCoefficient(
+        l.value,
+        w.value,
+        h.value,
+        GeneralFormulas.VolumeCofficient,
+      );
+      outputItem.value = value;
+      description = "${ref.description}  ${roundDouble(value)}cmm³";
+    } else {
+      description = ref.description;
+    }
+
+    outputItem.updateDescription(description: description);
+    output = outputItem;
+  }
+
+  OutputItem? _pickChildOutput(StraightLine item) {
+    if (item.calculator == null) return null;
+
+    if (item.calculator!.outputs.isEmpty) {
+      return item.calculator!.output;
+    } else {
+      return item.calculator!.outputs.first;
+    }
+  }
+}

+ 20 - 7
lib/process/items/factory.dart

@@ -2,6 +2,7 @@ import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/items/item_metas.dart';
 import 'package:fis_measure/interfaces/process/items/types.dart';
 import 'package:fis_measure/process/items/item.dart';
+import 'package:fis_measure/process/primitives/combos/lwh_straightline.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 
 /// 测量项创建器
@@ -27,24 +28,34 @@ class MeasureItemFactory {
 
   final _itemCreatorMap = <String, MeasureItemCreator>{};
 
-  void _register(String typeName, MeasureItemCreator creator) {
-    _itemCreatorMap[typeName] = creator;
+  void _register(String typeName, MeasureItemCreator creator,
+      [String? suffix]) {
+    final key = _buildKey(typeName, suffix);
+    _itemCreatorMap[key] = creator;
   }
 
-  MeasureItemCreator? _findCreator(String typeName) {
-    if (_itemCreatorMap.containsKey(typeName)) {
-      return _itemCreatorMap[typeName];
+  MeasureItemCreator? _findCreator(String typeName, [String? suffix]) {
+    final key = _buildKey(typeName, suffix);
+    if (_itemCreatorMap.containsKey(key)) {
+      return _itemCreatorMap[key];
     }
     return null;
   }
 
+  String _buildKey(String typeName, [String? suffix]) {
+    return suffix == null ? typeName : '${typeName}_$suffix';
+  }
+
   /// 创建测量项
   ///
   /// [meta] 元信息
   ///
   /// [parent] 父项
-  static MeasureItem? createItem(ItemMeta meta, IMeasureItem? parent) {
-    final creator = _singleton._findCreator(meta.measureType);
+  static MeasureItem? createItem(ItemMeta meta, [IMeasureItem? parent]) {
+    if (meta.measureType == null || meta.measureType!.isEmpty) return null;
+
+    final creator =
+        _singleton._findCreator(meta.measureType!, meta.multiMethod);
     if (creator == null) return null;
 
     final item = creator.call(meta, parent);
@@ -53,5 +64,7 @@ class MeasureItemFactory {
 
   static void _registerItemCreators() {
     _singleton._register(MeasureTypes.Distance, StraightLine.createDistance);
+    _singleton._register(
+        MeasureTypes.Volume, LWHStraightLine.createVolume, "LWH");
   }
 }

+ 30 - 1
lib/process/items/top_item.dart

@@ -21,22 +21,51 @@ abstract class TopMeasureItem<T extends MeasureItemFeature>
   @override
   IMeasureItem get workingChild => _childItems[_childIndex];
 
+  /// 子项全部完成
+  @protected
+  bool get childrenAllDone =>
+      _childItems.every((e) => e.measuredFeatures.isNotEmpty);
+
   @override
   late final FEventHandler<int> workingChildChanged;
 
   @override
   void switchChild(int index) {
+    if (index == _childIndex) return;
+
     _childIndex = index;
+    workingChildChanged.emit(this, index);
   }
 
   @protected
   void nextChild() {
     final count = _childItems.length;
+    int nextIdx = -1;
     for (var i = 0; i < count; i++) {
       final child = _childItems[i];
       if (child.measuredFeatures.isEmpty) {
-        switchChild(i);
+        nextIdx = i;
+        break;
       }
     }
+    if (nextIdx > -1) {
+      switchChild(nextIdx);
+    }
+  }
+
+  @protected
+  void listenChildrenUpdate() {
+    for (var item in childItems) {
+      item.featureUpdated.addListener((sender, e) {
+        _onChildFeatureUpdated();
+      });
+    }
+  }
+
+  void _onChildFeatureUpdated() {
+    if (workingChild.measuredFeatures.isNotEmpty) {
+      nextChild();
+    }
+    doFeatureUpdate();
   }
 }

+ 11 - 0
lib/process/items/top_item_feature.dart

@@ -0,0 +1,11 @@
+import 'package:fis_measure/interfaces/process/items/item.dart';
+
+import 'item_feature.dart';
+import 'top_item.dart';
+
+abstract class TopMeasureItemFeature extends MeasureItemFeature {
+  TopMeasureItemFeature(ITopMeasureItem refItem) : super(refItem);
+
+  @override
+  TopMeasureItem get refItem => super.refItem as TopMeasureItem;
+}

+ 53 - 9
lib/process/primitives/combos/lwh_straightline.dart

@@ -2,33 +2,77 @@ import 'dart:ui';
 
 import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/items/item_metas.dart';
+import 'package:fis_measure/interfaces/process/items/types.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
+import 'package:fis_measure/process/calcuators/volume.dart';
 import 'package:fis_measure/process/items/item_feature.dart';
 import 'package:fis_measure/process/items/top_item.dart';
+import 'package:fis_measure/process/items/top_item_feature.dart';
+import 'package:fis_measure/process/primitives/straightline.dart';
 
-class LWHStraightline extends TopMeasureItem<LWHStraightlineFeature> {
-  LWHStraightline(ItemMeta meta) : super(meta);
+class LWHStraightLine extends TopMeasureItem<LWHStraightlineFeature> {
+  static const String _lineLKey = "L";
+  static const String _lineWKey = "W";
+  static const String _lineHKey = "H";
+
+  late final StraightLine l;
+  late final StraightLine w;
+  late final StraightLine h;
+
+  LWHStraightLine(ItemMeta meta) : super(meta) {
+    final metaL = meta.getChildByName(_lineLKey)!;
+    final metaH = meta.getChildByName(_lineHKey)!;
+    final metaW = meta.getChildByName(_lineWKey)!;
+    l = StraightLine.createDistance(metaL, this);
+    w = StraightLine.createDistance(metaW, this);
+    h = StraightLine.createDistance(metaH, this);
+    childItems.add(l);
+    childItems.add(w);
+    childItems.add(h);
+    listenChildrenUpdate();
+    feature = LWHStraightlineFeature(this);
+  }
 
   @override
   bool onExecuteMouse(PointInfo args) {
-    // TODO: implement onExecuteMouse
-    throw UnimplementedError();
+    if (args.pointType == PointInfoType.mouseDown) {
+      if (childrenAllDone) {
+        workingChild.clear();
+      }
+    }
+    final result = workingChild.execute(args);
+    doCalculate();
+    return result;
   }
 
   @override
   bool onExecuteTouch(PointInfo args) {
-    // TODO: implement onExecuteTouch
-    throw UnimplementedError();
+    return workingChild.execute(args);
+  }
+
+  static LWHStraightLine createVolume(ItemMeta meta, [IMeasureItem? parent]) {
+    if (meta.measureType != MeasureTypes.Volume || meta.multiMethod != "LWH") {
+      throw ArgumentError();
+    }
+    var lwh = LWHStraightLine(meta);
+    lwh.calculator = VolumeThreeDistanceCal(lwh);
+
+    return lwh;
   }
 }
 
-class LWHStraightlineFeature extends MeasureItemFeature {
+class LWHStraightlineFeature extends TopMeasureItemFeature {
   LWHStraightlineFeature(
-    IMeasureItem refItem,
+    ITopMeasureItem refItem,
   ) : super(refItem);
 
   @override
   void paint(Canvas canvas, Size size) {
-    // TODO: implement paint
+    for (var item in refItem.childItems) {
+      for (var feature in item.measuredFeatures) {
+        feature.paint(canvas, size);
+      }
+      item.feature?.paint(canvas, size);
+    }
   }
 }

+ 34 - 4
lib/process/workspace/application.dart

@@ -16,6 +16,7 @@ import 'package:fis_measure/interfaces/process/workspace/recorder.dart';
 import 'package:fis_measure/process/annotations/arrow_annotation.dart';
 import 'package:fis_measure/process/annotations/input_annotation.dart';
 import 'package:fis_measure/process/annotations/label_annotation.dart';
+import 'package:fis_measure/process/items/factory.dart';
 import 'package:fis_measure/process/primitives/location.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 import 'package:fis_measure/process/visual/tissue_area.dart';
@@ -255,7 +256,7 @@ class Application implements IApplication {
       activeMeasureItem = StraightLine.createDistance(
         ItemMeta(
           MeasureTypes.Distance,
-          MeasureTypes.Distance,
+          measureType: MeasureTypes.Distance,
           description: MeasureTypes.Distance,
           briefAnnotation: "D",
           outputs: [
@@ -270,7 +271,7 @@ class Application implements IApplication {
       activeMeasureItem = PolyLine.createPerimeter(
         ItemMeta(
           MeasureTypes.Perimeter,
-          MeasureTypes.Perimeter,
+          measureType: MeasureTypes.Perimeter,
           description: MeasureTypes.Perimeter,
           briefAnnotation: MeasureTypes.Perimeter,
           outputs: [
@@ -285,7 +286,7 @@ class Application implements IApplication {
       activeMeasureItem = PolyLine.createArea(
         ItemMeta(
           MeasureTypes.Area,
-          MeasureTypes.Area,
+          measureType: MeasureTypes.Area,
           description: MeasureTypes.Area,
           briefAnnotation: MeasureTypes.Area,
           outputs: [
@@ -304,7 +305,7 @@ class Application implements IApplication {
       activeMeasureItem = fn(
         ItemMeta(
           MeasureTypes.Depth,
-          MeasureTypes.Depth,
+          measureType: MeasureTypes.Depth,
           description: MeasureTypes.Depth,
           briefAnnotation: MeasureTypes.Depth,
           outputs: [
@@ -316,6 +317,35 @@ class Application implements IApplication {
       return;
     }
 
+    if (name == MeasureTypes.Volume) {
+      final childBuild = (String name) {
+        return ItemMeta(
+          name,
+          description: name,
+          outputs: [
+            ItemOutputMeta("Distance", "Distance", VidUsUnit.cm),
+          ],
+        );
+      };
+      activeMeasureItem = MeasureItemFactory.createItem(
+        ItemMeta(
+          MeasureTypes.Volume,
+          measureType: MeasureTypes.Volume,
+          description: MeasureTypes.Volume,
+          outputs: [
+            ItemOutputMeta("Volume", "Volume", VidUsUnit.cm2),
+          ],
+          multiMethod: "LWH",
+          childItems: [
+            childBuild("L"),
+            childBuild("W"),
+            childBuild("H"),
+          ],
+        ),
+      );
+      return;
+    }
+
     activeMeasureItem = null;
   }