Browse Source

modify result design

melon.yin 2 years ago
parent
commit
46610c6a3e

+ 67 - 0
lib/interfaces/process/calculators/values.dart

@@ -0,0 +1,67 @@
+import 'package:fis_measure/interfaces/process/items/item_metas.dart';
+import 'package:vid/us/vid_us_unit.dart';
+
+/// 测量结果
+abstract class ValueBase {
+  /// 格式元
+  final ItemOutputMeta meta;
+
+  /// 数据单位
+  VidUsUnit unit;
+
+  ValueBase(this.meta, this.unit);
+
+  /// 计算结果名称
+  String get name => meta.name;
+
+  /// 目标单位
+  VidUsUnit get targetUnit => meta.unit;
+}
+
+abstract class TypedValueBase<T> extends ValueBase {
+  T? _value;
+
+  TypedValueBase(
+    ItemOutputMeta meta,
+    T? val,
+    VidUsUnit unit,
+  ) : super(meta, unit) {
+    _value = val;
+  }
+
+  /// 测量结果值
+  T? get value => _value;
+  set value(T? val) {
+    if (val != _value) {
+      _value = val;
+    }
+  }
+}
+
+/// 浮点值
+class FloatValue extends TypedValueBase<double> {
+  int _fractionalDigits = 2;
+
+  FloatValue(
+    ItemOutputMeta meta,
+    double? val,
+    VidUsUnit unit,
+  ) : super(meta, val, unit);
+
+  /// 小数精度
+  int get fractionalDigits => _fractionalDigits;
+  set fractionalDigits(int value) {
+    if (value != _fractionalDigits) {
+      _fractionalDigits = value;
+    }
+  }
+}
+
+/// 字符串值
+class StringValue extends TypedValueBase<String> {
+  StringValue(
+    ItemOutputMeta meta,
+    String? val,
+    VidUsUnit unit,
+  ) : super(meta, val, unit);
+}

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

@@ -1,4 +1,5 @@
 import 'package:fis_measure/interfaces/date_types/point.dart';
+import 'package:fis_measure/interfaces/process/calculators/values.dart';
 import 'package:flutter/rendering.dart';
 import 'item.dart';
 
@@ -16,6 +17,12 @@ abstract class IMeasureItemFeature {
   /// 序号
   int get id;
 
+  /// 计算结果列表
+  List<ValueBase> get values;
+
+  /// 结果值
+  ValueBase? get value;
+
   /// 绘制
   void paint(Canvas canvas, Size size);
 }

+ 17 - 1
lib/interfaces/process/items/item_metas.dart

@@ -53,5 +53,21 @@ class ItemOutputMeta {
   /// 单位
   VidUsUnit unit;
 
-  ItemOutputMeta(this.name, this.description, this.unit);
+  /// 简介注释
+  String? briefAnnotation;
+
+  /// 额外注释
+  String? additionalAnnotation;
+
+  /// 小数精度
+  int fractionalDigits;
+
+  ItemOutputMeta(
+    this.name,
+    this.description,
+    this.unit, {
+    this.briefAnnotation,
+    this.additionalAnnotation,
+    this.fractionalDigits = 2,
+  });
 }

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

@@ -1,6 +1,7 @@
 // ignore_for_file: constant_identifier_names
 
 class MeasureTypes {
+  MeasureTypes._();
   /* Tissue */
 
   /// 深度
@@ -20,4 +21,10 @@ class MeasureTypes {
 
   /// 角度
   static const String Angle = "Angle";
+
+  /// 椭圆测面积周长
+  static const String AreaPerimeterEllipse = "AreaPerimeterEllipse";
+
+  /// 椭圆测体积
+  static const String VolumeEllipse = "VolumeEllipse";
 }

+ 7 - 0
lib/measure_page_test.dart

@@ -2,6 +2,7 @@ import 'dart:typed_data';
 
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:fis_measure/interfaces/enums/annotation.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:fis_measure/interfaces/process/items/types.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
 import 'package:fis_measure/interfaces/process/standard_line/calibration.dart';
@@ -306,6 +307,7 @@ class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
     MeasureTypes.Area,
     MeasureTypes.Depth,
     MeasureTypes.Volume,
+    MeasureTypes.AreaPerimeterEllipse,
   ];
 
   final scrollController = ScrollController();
@@ -404,6 +406,11 @@ class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
     });
     final name = C_SUPPORTED_ITEMS[index];
     application.switchItemByName(name);
+    // handle combo item
+    final item = application.activeMeasureItem!;
+    if (item is ITopMeasureItem) {
+      item.switchChild(0);
+    }
   }
 }
 

+ 2 - 0
lib/process/calcuators/area.dart

@@ -25,6 +25,8 @@ class PolyLineAreaCal extends Calculator<PolyLine, double> {
     final description = "${ref.description}: ${roundDouble(value)}cm²";
     outputItem.updateDescription(description: description);
     output = outputItem;
+
+    updateFloatValue(value);
   }
 
   static double clacArea(List<DPoint> points) {

+ 35 - 0
lib/process/calcuators/calculator.dart

@@ -2,6 +2,7 @@ import 'package:fis_common/event/event_type.dart';
 import 'package:fis_measure/interfaces/process/calculators/calculator.dart';
 import 'package:fis_measure/interfaces/process/calculators/output.dart';
 import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/process/items/item_feature.dart';
 import 'package:flutter/foundation.dart';
 import 'package:vid/us/vid_us_unit.dart';
 
@@ -41,6 +42,14 @@ abstract class Calculator<T extends IMeasureItem, TValue>
     }
   }
 
+  @protected
+  void updateFloatValue(double value) {
+    if (ref.feature == null) return;
+    final feature = ref.feature! as MeasureItemFeature;
+    final viewport = feature.hostVisualArea!.viewport!;
+    feature.updateFloatValue(ref.meta.outputs.first, value, viewport.xUnit);
+  }
+
   /// 对double四舍五入
   double roundDouble(double value, [int digits = 2]) {
     final digitsStr = value.toStringAsFixed(digits);
@@ -58,4 +67,30 @@ abstract class Calculator<T extends IMeasureItem, TValue>
       unit: unit,
     );
   }
+
+  /// 获取结果精度
+  @protected
+  int getResultDigits(OutputItem item) {
+    int fractionalDigits = 2;
+
+    VidUsUnit? targetUnit = item.unit;
+    switch (targetUnit) {
+      case VidUsUnit.mm:
+        fractionalDigits = 3;
+        break;
+      case VidUsUnit.mm2:
+        fractionalDigits = 4;
+        break;
+      case VidUsUnit.mm3:
+        fractionalDigits = 5;
+        break;
+      case VidUsUnit.msec:
+        fractionalDigits = 3;
+        break;
+      default:
+        // fractionalDigits = GetConfigDigits(item);
+        break;
+    }
+    return fractionalDigits;
+  }
 }

+ 8 - 6
lib/process/calcuators/depth.dart

@@ -18,13 +18,15 @@ class TissueDepthCal extends Calculator<Location, double> {
     final viewport = ref.feature!.hostVisualArea!.viewport!;
     final point = ref.feature!.innerPoints.first;
     final physicalPoint = viewport.physical!.convert(point);
-    final depth = physicalPoint.y * viewport.region.height;
+    final value = physicalPoint.y * viewport.region.height;
 
-    final outputItem = createOutput(MeasureTypes.Depth, depth, VidUsUnit.cm);
+    final outputItem = createOutput(MeasureTypes.Depth, value, VidUsUnit.cm);
     final description =
-        "${ref.description}: ${roundDouble(depth)}${VidUsUnit.cm.name}";
+        "${ref.description}: ${roundDouble(value)}${VidUsUnit.cm.name}";
     outputItem.updateDescription(description: description);
     output = outputItem;
+
+    updateFloatValue(value);
   }
 }
 
@@ -52,11 +54,11 @@ class TissueConvexDepthCal extends Calculator<Location, double> {
         depth < 0 ? '' : '${roundDouble(depth)}${VidUsUnit.cm.name}';
     final description = "${ref.description}: $valueDesc";
 
-    final double outputValue = depth < 0 ? 0 : depth;
-    final outputItem =
-        createOutput(MeasureTypes.Depth, outputValue, VidUsUnit.cm);
+    final double value = depth < 0 ? 0 : depth;
+    final outputItem = createOutput(MeasureTypes.Depth, value, VidUsUnit.cm);
 
     outputItem.updateDescription(description: description);
     output = outputItem;
+    updateFloatValue(value);
   }
 }

+ 6 - 3
lib/process/calcuators/distance.dart

@@ -13,10 +13,11 @@ class DistanceCal extends Calculator<StraightLine, double> {
   void calculate() {
     if (ref.feature == null) return;
 
+    final feature = ref.feature!;
     // TODO:xxx
-    final viewport = ref.feature!.hostVisualArea!.viewport!;
-    final p1 = ref.feature!.startPoint;
-    final p2 = ref.feature!.endPoint;
+    final viewport = feature.hostVisualArea!.viewport!;
+    final p1 = feature.startPoint;
+    final p2 = feature.endPoint;
     final pp1 = viewport.convert(p1);
     final pp2 = viewport.convert(p2);
     // final pp1 =
@@ -31,5 +32,7 @@ class DistanceCal extends Calculator<StraightLine, double> {
         "${ref.description}: ${roundDouble(value)}${VidUsUnit.cm.name}";
     outputItem.updateDescription(description: description);
     output = outputItem;
+
+    updateFloatValue(value);
   }
 }

+ 13 - 3
lib/process/calcuators/perimeter.dart

@@ -15,9 +15,9 @@ class PolyLinePerimeterCal extends Calculator<PolyLine, double> {
     if (ref.feature == null) return;
 
     // TODO:xxx
-    final viewport = ref.feature!.hostVisualArea!.viewport!;
-    final points =
-        ref.feature!.innerPoints.map((e) => viewport.convert(e)).toList();
+    final feature = ref.feature!;
+    final viewport = feature.hostVisualArea!.viewport!;
+    final points = feature.innerPoints.map((e) => viewport.convert(e)).toList();
 
     double threshold = 0;
     // if (ref.Threshold.HasValue)
@@ -31,6 +31,7 @@ class PolyLinePerimeterCal extends Calculator<PolyLine, double> {
         "${ref.description}: ${roundDouble(value)}${VidUsUnit.cm.name}";
     outputItem.updateDescription(description: description);
     output = outputItem;
+    updateFloatValue(value);
   }
 
   static double calcPerimeter(
@@ -56,3 +57,12 @@ class PolyLinePerimeterCal extends Calculator<PolyLine, double> {
     return sum;
   }
 }
+
+class AreaPerimeterEllipseCal extends Calculator<Ellipse, double> {
+  AreaPerimeterEllipseCal(Ellipse ref) : super(ref);
+
+  @override
+  void calculate() {
+    // TODO: implement calculate
+  }
+}

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

@@ -1,6 +1,7 @@
 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/ellipse.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 
 import 'calculator.dart';
@@ -19,6 +20,8 @@ class VolumeThreeDistanceCal extends Calculator<LWHStraightLine, double> {
     final h = _pickChildOutput(ref.h);
 
     String description;
+    final feature = ref.feature!;
+    final viewport = feature.hostVisualArea!.viewport!;
     if (l != null && w != null && h != null) {
       final value = GeneralFormulas.volumeWithCoefficient(
         l.value,
@@ -26,6 +29,7 @@ class VolumeThreeDistanceCal extends Calculator<LWHStraightLine, double> {
         h.value,
         GeneralFormulas.VolumeCofficient,
       );
+      updateFloatValue(value);
       outputItem.value = value;
       description = "${ref.description}  ${roundDouble(value)}cm³";
     } else {
@@ -46,3 +50,12 @@ class VolumeThreeDistanceCal extends Calculator<LWHStraightLine, double> {
     }
   }
 }
+
+class VolumeEllipseCal extends Calculator<Ellipse, double> {
+  VolumeEllipseCal(Ellipse ref) : super(ref);
+
+  @override
+  void calculate() {
+    // TODO: implement calculate
+  }
+}

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

@@ -3,6 +3,7 @@ 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/ellipse.dart';
 import 'package:fis_measure/process/primitives/straightline.dart';
 
 /// 测量项创建器
@@ -17,10 +18,12 @@ typedef MeasureItemCreator = MeasureItem Function(
 
 /// 测量项工厂
 class MeasureItemFactory {
+  MeasureItemFactory._();
+
   static MeasureItemFactory? _singletonInstance;
   static MeasureItemFactory get _singleton {
     if (_singletonInstance == null) {
-      _singletonInstance = MeasureItemFactory();
+      _singletonInstance = MeasureItemFactory._();
       _registerItemCreators();
     }
     return _singletonInstance!;
@@ -66,5 +69,10 @@ class MeasureItemFactory {
     _singleton._register(MeasureTypes.Distance, StraightLine.createDistance);
     _singleton._register(
         MeasureTypes.Volume, LWHStraightLine.createVolume, "LWH");
+
+    //Ellipse
+    _singleton._register(
+        MeasureTypes.AreaPerimeterEllipse, Ellipse.createAreaPerimeter);
+    _singleton._register(MeasureTypes.VolumeEllipse, Ellipse.createVolume);
   }
 }

+ 37 - 3
lib/process/items/item_feature.dart

@@ -1,8 +1,10 @@
 import 'dart:ui';
 
 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/item.dart';
 import 'package:fis_measure/interfaces/process/items/item_feature.dart';
+import 'package:fis_measure/interfaces/process/items/item_metas.dart';
 import 'package:fis_measure/interfaces/process/visuals/visual_area.dart';
 import 'package:fis_measure/utils/canvas.dart';
 import 'package:fis_measure/values/colors.dart';
@@ -10,13 +12,15 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/painting.dart';
 import 'package:flutter/widgets.dart';
 import 'package:get/get.dart';
+import 'package:vid/us/vid_us_unit.dart';
 
 abstract class MeasureItemFeature implements IMeasureItemFeature {
   late IMeasureItem _refItem;
   late List<DPoint> _innerPoints;
   IVisualArea? _hostVisualArea;
   bool _isActive = true;
-  late final int _id;
+  int _id = 0;
+  final List<ValueBase> _values = [];
 
   @protected
   final paintPan = Paint()
@@ -27,7 +31,12 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
 
   MeasureItemFeature(IMeasureItem refItem) {
     _refItem = refItem;
-    _id = refItem.assignId();
+    if (refItem.parent != null) {
+      _id = refItem.parent!.feature?.id ?? 0;
+    } else {
+      _id = refItem.assignId();
+    }
+
     _innerPoints = [];
   }
   @override
@@ -58,6 +67,25 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
   /// 顶点尺寸
   double get vertexSize => 10.0 * refItem.scaleRatio;
 
+  @override
+  List<ValueBase> get values => _values;
+
+  @override
+  ValueBase? get value => _values.isNotEmpty ? _values.first : null;
+
+  void updateFloatValue(
+      ItemOutputMeta outputMeta, double value, VidUsUnit unit) {
+    final valueBase = values.firstWhereOrNull((e) => e.name == outputMeta.name);
+    if (valueBase == null) {
+      final floatValue = FloatValue(outputMeta, value, unit);
+      values.add(floatValue);
+    } else {
+      final floatValue = valueBase as FloatValue;
+      floatValue.value = value;
+      floatValue.unit = unit;
+    }
+  }
+
   @protected
   DPoint convert2ViewPoint(Size size, DPoint logicPoint) {
     final x = size.width * logicPoint.x;
@@ -70,7 +98,13 @@ abstract class MeasureItemFeature implements IMeasureItemFeature {
   /// [text] 自定义序号内容
   @protected
   void drawId(Canvas canvas, Size size, [String? text]) {
-    final displayText = text ?? id.toString();
+    final String displayText;
+    if (refItem.parent == null) {
+      displayText = text ?? id.toString();
+    } else {
+      displayText = '$id.${refItem.description}';
+    }
+
     final fontSize = 14.0 * refItem.scaleRatio; // TODO: from config
     final letterSpacing = 0.0 * refItem.scaleRatio;
     final style = TextStyle(

+ 1 - 0
lib/process/primitives/combos/lwh_straightline.dart

@@ -40,6 +40,7 @@ class LWHStraightLine extends TopMeasureItem<LWHStraightlineFeature> {
         workingChild.clear();
       }
     }
+    feature!.hostVisualArea = args.hostVisualArea;
     final result = workingChild.execute(args);
     doCalculate();
     return result;

+ 101 - 2
lib/process/primitives/ellipse.dart

@@ -1,7 +1,13 @@
 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/perimeter.dart';
+import 'package:fis_measure/process/calcuators/volume.dart';
+import 'package:fis_measure/utils/canvas.dart';
+import 'package:path_drawing/path_drawing.dart';
 import '../items/item.dart';
 import '../items/item_feature.dart';
 
@@ -10,6 +16,33 @@ class Ellipse extends MeasureItem<EllipseFeature> {
 
   @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;
+      if (args.pointType == PointInfoType.mouseUp) return false;
+
+      final points = feature!.innerPoints;
+      if (points.length < 2) {
+        points.add(args);
+      } else if (points.length < 3) {
+        points[1] = args;
+      } else {
+        points[2] = args;
+      }
+      doCalculate();
+      if (points.length == 3 && args.pointType == PointInfoType.mouseDown) {
+        doFeatureFinish();
+      }
+    }
     return true;
   }
 
@@ -17,11 +50,77 @@ class Ellipse extends MeasureItem<EllipseFeature> {
   bool onExecuteTouch(PointInfo args) {
     return true;
   }
+
+  void handleMouseDownWhileWaiting(PointInfo args) {
+    // TODO: 判断是否当前area
+    // 转换为Area逻辑位置
+    final point = args.toAreaLogicPoint();
+    feature = EllipseFeature(this, point);
+    if (args.hostVisualArea != null) {
+      feature!.hostVisualArea = args.hostVisualArea;
+    }
+    state = ItemStates.running;
+  }
+
+  static Ellipse createAreaPerimeter(ItemMeta meta, [IMeasureItem? parent]) {
+    final ellipse = Ellipse(meta, parent);
+    ellipse.calculator = AreaPerimeterEllipseCal(ellipse);
+    return ellipse;
+  }
+
+  static Ellipse createVolume(ItemMeta meta, [IMeasureItem? parent]) {
+    final ellipse = Ellipse(meta, parent);
+    ellipse.calculator = VolumeEllipseCal(ellipse);
+    return ellipse;
+  }
 }
 
 class EllipseFeature extends MeasureItemFeature {
-  EllipseFeature(IMeasureItem refItem) : super(refItem);
+  EllipseFeature(IMeasureItem refItem, DPoint point) : super(refItem) {
+    innerPoints.add(point);
+  }
+
+  /// 质心
+  DPoint get centroid {
+    final x = (innerPoints[1].x + innerPoints[0].x) * 0.5;
+    final y = (innerPoints[1].y + innerPoints[0].y) * 0.5;
+    return DPoint(x, y);
+  }
 
   @override
-  void paint(Canvas canvas, Size size) {}
+  void paint(Canvas canvas, Size size) {
+    if (innerPoints.isEmpty) return;
+
+    var idText = '$id.${refItem.briefAnnotation}';
+    drawId(canvas, size, idText);
+
+    final startOffset = convert2ViewPoint(size, innerPoints.first).toOffset();
+
+    if (innerPoints.length < 2) {
+      drawVertex(canvas, startOffset, true);
+      return;
+    }
+    drawVertex(canvas, startOffset);
+
+    final secondOffset = convert2ViewPoint(size, innerPoints[1]).toOffset();
+    canvas.drawDashLine(startOffset, secondOffset, 1, 10, paintPan);
+
+    final radius = (secondOffset - startOffset).distance.abs() / 2;
+    final centroidOffset = convert2ViewPoint(size, centroid).toOffset();
+    canvas.drawCircle(centroidOffset, radius, paintPan);
+    // canvas.drawOval(rect, paint)
+    // final path = Path();
+    // path.moveTo(centroidOffset.dx, centroidOffset.dy);
+    // path.conicTo(x1, y1, x2, y2, w)
+    // canvas.drawPath(
+    //   dashPath(path, dashArray: CircularIntervalList([2.0, 10.0])),
+    //   paintPan,
+    // );
+
+    if (innerPoints.length < 3) {
+      drawVertex(canvas, secondOffset, true);
+      return;
+    }
+    drawVertex(canvas, secondOffset);
+  }
 }

+ 16 - 1
lib/process/workspace/application.dart

@@ -250,6 +250,7 @@ class Application implements IApplication {
   @override
   void switchItem(ItemMeta meta) {
     switchItemByName(meta.name);
+    activeMeasureItem ??= MeasureItemFactory.createItem(meta);
   }
 
   @override
@@ -338,7 +339,7 @@ class Application implements IApplication {
           measureType: MeasureTypes.Volume,
           description: MeasureTypes.Volume,
           outputs: [
-            ItemOutputMeta("Volume", "Volume", VidUsUnit.cm2),
+            ItemOutputMeta("Volume", "Volume", VidUsUnit.cm3),
           ],
           multiMethod: "LWH",
           childItems: [
@@ -351,6 +352,20 @@ class Application implements IApplication {
       return;
     }
 
+    if (name == MeasureTypes.AreaPerimeterEllipse) {
+      activeMeasureItem = MeasureItemFactory.createItem(
+        ItemMeta(
+          MeasureTypes.AreaPerimeterEllipse,
+          measureType: MeasureTypes.AreaPerimeterEllipse,
+          description: MeasureTypes.AreaPerimeterEllipse,
+          outputs: [
+            ItemOutputMeta("Perimeter", "Perimeter", VidUsUnit.cm2),
+          ],
+        ),
+      );
+      return;
+    }
+
     activeMeasureItem = null;
   }
 

+ 5 - 0
lib/process/workspace/recorder.dart

@@ -56,6 +56,7 @@ class MeasureRecorder implements IMeasureRecorder {
     for (var i = 0; i < len; i++) {
       undoOnce();
     }
+    _resetId();
   }
 
   bool _undoOnceMeasure(_MeasureModel record) {
@@ -85,6 +86,10 @@ class MeasureRecorder implements IMeasureRecorder {
     return true;
   }
 
+  void _resetId() {
+    _lastId = 0;
+  }
+
   void _increaseId() {
     _lastId++;
   }

+ 124 - 42
lib/view/result/results_panel.dart

@@ -1,9 +1,13 @@
 import 'dart:ui' as ui;
 import 'package:fis_measure/interfaces/process/calculators/output.dart';
+import 'package:fis_measure/interfaces/process/calculators/values.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/items/item_feature.dart';
 import 'package:fis_measure/interfaces/process/modes/mode.dart';
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
 import 'package:fis_measure/process/items/item_feature.dart';
 import 'package:fis_measure/values/colors.dart';
+import 'package:fis_measure/values/unit_desc.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
@@ -19,11 +23,9 @@ class _MeasureResultPanelState extends State<MeasureResultPanel> {
   final _scrollController = ScrollController();
   final application = Get.find<IApplication>();
 
-  final List<OutputItem> outputs = [];
-  final _outputMap = <IMode, List<OutputItem>>{};
-  int idStrLength = 1;
+  final List<String> lines = [];
 
-  bool get hasOutputs => outputs.isNotEmpty;
+  bool get hasOutputs => lines.isNotEmpty;
 
   @override
   Widget build(BuildContext context) {
@@ -109,27 +111,14 @@ class _MeasureResultPanelState extends State<MeasureResultPanel> {
   }
 
   List<FWidget> _buildMainList() {
-    final list = <FWidget>[];
-    list.addAll(_buildModeOutput(application.applicationName, outputs));
-    return list;
-  }
-
-  List<FWidget> _buildModeOutput(
-      String title, List<OutputItem<dynamic>> dataList) {
     final list = <FWidget>[
       const FSizedBox(height: 6),
-      _buildTitle(title),
+      _buildTitle(application.applicationName),
       _buildTitleDivider(),
     ];
-
-    final length = dataList.length;
-    for (var i = 0; i < length; i++) {
-      final output = dataList[i];
-      final idStr = _getIdDisplayStr(output.id);
-      list.add(const FSizedBox(height: 4));
-      list.add(_buildLine('$idStr ${output.description}'));
+    for (var line in lines) {
+      list.add(_buildLine(line));
     }
-
     return list;
   }
 
@@ -171,41 +160,134 @@ class _MeasureResultPanelState extends State<MeasureResultPanel> {
   }
 
   void _updateOutputs() {
-    outputs.clear();
-
+    lines.clear();
+    final features = <IMeasureItemFeature>[];
     for (var item in application.measureItems) {
-      for (var feature in item.measuredFeatures) {
-        (feature as MeasureItemFeature).hostVisualArea?.mode;
+      if (item.measuredFeatures.isNotEmpty) {
+        features.addAll(item.measuredFeatures);
+      }
+      if (item.feature != null) {
+        features.add(item.feature!);
       }
     }
-    for (var item in application.measureItems) {
-      if (item.calculator != null) {
-        // 添加历史测量值
-        outputs.addAll(item.calculator!.outputs);
+    final idLength = features.length.toString().length;
+    for (var feature in features) {
+      final strList = _FeatureDescConverter(feature).generate(idLength);
+      lines.addAll(strList);
+    }
+  }
+
+  void _autoScrollToBottom() {
+    if (lines.isEmpty) return;
+    Future.delayed(const Duration(milliseconds: 200), () {
+      if (_scrollController.hasClients) {
+        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
+      }
+    });
+  }
+}
+
+class _FeatureDescConverter {
+  final IMeasureItemFeature feature;
+  _FeatureDescConverter(this.feature);
+
+  String idPlaceStr = ' ';
+  String idStr = ' ';
+
+  List<String> generate(int idLength) {
+    idPlaceStr = ' '.padRight(idLength, ' ');
+    idStr = feature.id.toString().padRight(idLength, ' ');
+
+    final List<String> arr = [];
+    final mainLines = _findFeatureLines(feature);
+    arr.addAll(mainLines);
 
-        if (item.feature?.isActive == true && item.calculator!.output != null) {
-          // 添加活动测量值
-          outputs.add(item.calculator!.output!);
+    final ref = feature.refItem;
+    if (ref is ITopMeasureItem) {
+      for (var child in ref.childItems) {
+        for (var feature in child.measuredFeatures) {
+          final str = _findChildLine(feature);
+          if (str != null && str.isNotEmpty) {
+            arr.add(str);
+          }
+        }
+        if (child.feature != null) {
+          final str = _findChildLine(child.feature!);
+          if (str != null && str.isNotEmpty) {
+            arr.add(str);
+          }
         }
       }
     }
-    _checkIdStrLength();
+
+    return arr;
   }
 
-  void _checkIdStrLength() {
-    idStrLength = outputs.length.toString().length;
+  String? _findChildLine(IMeasureItemFeature feature) {
+    if (feature.value == null) return null;
+
+    final value = feature.value!;
+    final valueStr = _pickValueStr(value);
+
+    final name = feature.refItem.description;
+    final unitStr = UnitDescriptionMap.getDesc(value.meta.unit);
+    // final output = feature.refItem.meta.outputs.first;
+    // final name =
+    //     _findDisplayName(output.description, output.briefAnnotation, false);
+
+    return ' $idPlaceStr $name: $valueStr$unitStr';
+  }
+
+  List<String> _findFeatureLines(IMeasureItemFeature feature) {
+    final count = feature.values.length;
+    if (count == 0) {
+      final output = feature.refItem.meta.outputs.first;
+      final name =
+          _findDisplayName(output.description, output.briefAnnotation, false);
+      return ['$idStr $name'];
+    }
+
+    List<String> arr = [];
+    for (var i = 0; i < count; i++) {
+      final val = feature.values[i];
+      final meta = val.meta;
+      final valueStr = _pickValueStr(val);
+      final name =
+          _findDisplayName(meta.description, meta.briefAnnotation, false);
+      final unitStr = UnitDescriptionMap.getDesc(meta.unit);
+
+      if (i == 0) {
+        arr.add('$idStr $name: $valueStr$unitStr');
+      } else {
+        arr.add('$idPlaceStr $name: $valueStr$unitStr');
+      }
+    }
+
+    return arr;
   }
 
-  String _getIdDisplayStr(int id) {
-    return id.toString().padRight(idStrLength, ' ');
+  String _pickValueStr(ValueBase value) {
+    if (value is FloatValue) {
+      return _roundDouble(value.value!, value.meta.fractionalDigits).toString();
+    } else if (value is StringValue) {
+      return value.value!;
+    }
+    return '';
   }
 
-  void _autoScrollToBottom() {
-    if (outputs.isEmpty) return;
-    Future.delayed(const Duration(milliseconds: 200), () {
-      if (_scrollController.hasClients) {
-        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
+  String _findDisplayName(String description, String? briefAnnotation,
+      [bool showAnnotation = false]) {
+    if (showAnnotation) {
+      if (briefAnnotation != null && briefAnnotation.isNotEmpty) {
+        return briefAnnotation;
       }
-    });
+    }
+    return description;
+  }
+
+  double _roundDouble(double value, [int digits = 2]) {
+    final digitsStr = value.toStringAsFixed(digits);
+    final result = double.parse(digitsStr);
+    return result;
   }
 }