瀏覽代碼

1、ai图像优化

bakamaka.guan 2 年之前
父節點
當前提交
18062698b2

+ 44 - 20
lib/process/workspace/measure_data_controller.dart

@@ -1,16 +1,19 @@
 import 'package:fis_common/event/event_type.dart';
 import 'package:fis_jsonrpc/rpc.dart';
 
-/// 图片信息
+/// 图片信息数据
 abstract class IMeasureDataController {
-  late FEventHandler<String> measuredAIResultsInfoChanged;
+  late FEventHandler<String> aiResultsInfoChanged;
   late FEventHandler<List<RemedicalInfoDTO>> remedicalListResultChanged;
   late FEventHandler<String> itemCurrentImageChanged;
   late FEventHandler<List<ItemMetaDTO>> itemMetaListChanged;
+  late FEventHandler<List<String>> getMeasureApplicationChanged;
 
+  /// 该图像下面的应用
   List<ItemMetaDTO> get itemMetaList;
   set itemMetaList(List<ItemMetaDTO> value);
 
+  ///当前选择图像
   String get itemCurrentImage;
   set itemCurrentImage(String value);
 
@@ -21,14 +24,43 @@ abstract class IMeasureDataController {
   /// 测量图片集
   List<RemedicalInfoDTO> get remedicalList;
   set remedicalList(List<RemedicalInfoDTO> value);
+
+  /// 测量方式集
+  List<String> get getMeasureApplicationList;
+  set getMeasureApplicationList(List<String> value);
 }
 
 class MeasureDataController implements IMeasureDataController {
-  String _measuredResultsInfo = '';
+  String _aiResults = '';
   String _itemCurrentImage = '';
   List<RemedicalInfoDTO> _remedicalList = [];
-
   List<ItemMetaDTO> _itemMetaList = [];
+  List<String> _getMeasureApplicationList = [];
+
+  @override
+  var remedicalListResultChanged = FEventHandler<List<RemedicalInfoDTO>>();
+
+  @override
+  var aiResultsInfoChanged = FEventHandler<String>();
+
+  @override
+  var itemMetaListChanged = FEventHandler<List<ItemMetaDTO>>();
+
+  @override
+  var itemCurrentImageChanged = FEventHandler<String>();
+
+  @override
+  var getMeasureApplicationChanged = FEventHandler<List<String>>();
+
+  @override
+  List<String> get getMeasureApplicationList => _getMeasureApplicationList;
+  @override
+  set getMeasureApplicationList(List<String> value) {
+    if (value != _getMeasureApplicationList) {
+      _getMeasureApplicationList = value;
+      _onGetMeasureApplicationChanged();
+    }
+  }
 
   @override
   List<ItemMetaDTO> get itemMetaList => _itemMetaList;
@@ -40,9 +72,6 @@ class MeasureDataController implements IMeasureDataController {
     }
   }
 
-  @override
-  var itemMetaListChanged = FEventHandler<List<ItemMetaDTO>>();
-
   @override
   String get itemCurrentImage => _itemCurrentImage;
   @override
@@ -54,14 +83,11 @@ class MeasureDataController implements IMeasureDataController {
   }
 
   @override
-  var itemCurrentImageChanged = FEventHandler<String>();
-
-  @override
-  String get aiResults => _measuredResultsInfo;
+  String get aiResults => _aiResults;
   @override
   set aiResults(String value) {
-    if (value != _measuredResultsInfo) {
-      _measuredResultsInfo = value;
+    if (value != _aiResults) {
+      _aiResults = value;
       _onAIResultsChanged();
     }
   }
@@ -76,12 +102,6 @@ class MeasureDataController implements IMeasureDataController {
     }
   }
 
-  @override
-  var remedicalListResultChanged = FEventHandler<List<RemedicalInfoDTO>>();
-
-  @override
-  var measuredAIResultsInfoChanged = FEventHandler<String>();
-
   MeasureDataController();
 
   void _onRemedicalListChanged() {
@@ -89,7 +109,7 @@ class MeasureDataController implements IMeasureDataController {
   }
 
   void _onAIResultsChanged() {
-    measuredAIResultsInfoChanged.emit(this, aiResults);
+    aiResultsInfoChanged.emit(this, aiResults);
   }
 
   void _onItemCurrentImageChanged() {
@@ -99,4 +119,8 @@ class MeasureDataController implements IMeasureDataController {
   void _onItemMetaListChanged() {
     itemMetaListChanged.emit(this, itemMetaList);
   }
+
+  void _onGetMeasureApplicationChanged() {
+    getMeasureApplicationChanged.emit(this, getMeasureApplicationList);
+  }
 }

+ 89 - 0
lib/process/workspace/measure_handler.dart

@@ -0,0 +1,89 @@
+import 'dart:core';
+
+import 'package:fis_common/event/event_type.dart';
+
+class ChangeImageInfo {
+  /// 图片地址
+  final String? imageUrl;
+
+  /// 图像编码
+  final String? remedicalCode;
+  ChangeImageInfo(
+    this.imageUrl,
+    this.remedicalCode,
+  );
+}
+
+class MeasureModeSelection {
+  /// 应用名称:读取Vid获取应用
+  final String? applicationName;
+
+  /// 分类名称:读取Vid获取应用分类名称
+  final String? categoryName;
+
+  /// 测量模式集合:读取Vid获取应用的模式集合,返回对应测量数据
+  final List<String>? measureModes;
+  MeasureModeSelection(
+    this.applicationName,
+    this.categoryName,
+    this.measureModes,
+  );
+}
+
+abstract class IMeasureHandler {
+  /// 测量切换图片事件
+  late FEventHandler<ChangeImageInfo> onImageChanged;
+
+  /// 切换图像传递数据
+  ChangeImageInfo get imageChanged;
+  set imageChanged(ChangeImageInfo value);
+
+  /// 测量方式选择
+  late FEventHandler<MeasureModeSelection> onMeasureModeSelectionChanged;
+
+  /// 切换图像测量方式变更数据
+  MeasureModeSelection get measureModeChanged;
+  set measureModeChanged(MeasureModeSelection value);
+}
+
+class MeasureHandler implements IMeasureHandler {
+  ChangeImageInfo _imageChanged = ChangeImageInfo(null, null);
+  MeasureModeSelection _measureModeChanged =
+      MeasureModeSelection(null, null, null);
+
+  @override
+  var onImageChanged = FEventHandler<ChangeImageInfo>();
+
+  @override
+  var onMeasureModeSelectionChanged = FEventHandler<MeasureModeSelection>();
+
+  @override
+  ChangeImageInfo get imageChanged => _imageChanged;
+
+  @override
+  MeasureModeSelection get measureModeChanged => _measureModeChanged;
+
+  @override
+  set imageChanged(ChangeImageInfo value) {
+    if (value != _imageChanged) {
+      _imageChanged = value;
+      _onImageChanged();
+    }
+  }
+
+  @override
+  set measureModeChanged(MeasureModeSelection value) {
+    if (value != _measureModeChanged) {
+      _measureModeChanged = value;
+      _onMeasureModeSelectionChanged();
+    }
+  }
+
+  void _onImageChanged() {
+    onImageChanged.emit(this, imageChanged);
+  }
+
+  void _onMeasureModeSelectionChanged() {
+    onMeasureModeSelectionChanged.emit(this, measureModeChanged);
+  }
+}

+ 2 - 3
lib/view/main/desktop.dart

@@ -107,9 +107,8 @@ class _MeasureMainViewState extends State<MeasureMainView> {
                   if (measureData.aiResults.isNotEmpty) ...[
                     LayoutId(
                       id: _LayerLayoutIds.paintAI,
-                      child: Container(
-                        child: AIPaintInfo(
-                            playerController as VidPlayerController),
+                      child: AIPaintInfo(
+                        playerController as VidPlayerController,
                       ),
                     )
                   ],

+ 174 - 22
lib/view/measure/measure_images_bar.dart

@@ -1,11 +1,159 @@
 import 'package:fis_jsonrpc/rpc.dart';
+import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
+import 'package:fis_measure/process/workspace/measure_controller.dart';
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
+import 'package:fis_measure/process/workspace/measure_handler.dart';
 import 'package:fis_measure/view/measure/measure_images_bar.dart';
 export 'package:fis_lib_business_components/index.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
+class MeasureImagesBar extends StatefulWidget implements FWidget {
+  const MeasureImagesBar({Key? key}) : super(key: key);
+
+  @override
+  State<MeasureImagesBar> createState() => _MeasureImagesBarState();
+}
+
+class _MeasureImagesBarState extends State<MeasureImagesBar> {
+  /// 图片滑动控制器
+  ScrollController scrollController = ScrollController();
+
+  /// 数据
+  final measureData = Get.find<MeasureDataController>();
+
+  /// 当前可滑动的范围宽度
+  late double width = 0;
+
+  /// 是否滑到最后
+  bool _isEnd = false;
+
+  /// 计算下标
+  int _currentIndex = 0;
+
+  /// 左边能否滑动
+  bool get isLeftCanScroll => _currentIndex <= 0;
+
+  /// 右边能否滑动
+  bool get isRightCanScroll => _isEnd;
+
+  /// 滑动区域的key值
+  final GlobalKey globalKey = GlobalKey();
+
+  /// 图片左划事件
+  void onLeftMoveImage() async {
+    if (_currentIndex > 0) {
+      _currentIndex--;
+      await scrollController.animateTo(
+        (160 * _currentIndex).toDouble(),
+        duration: const Duration(milliseconds: 300),
+        curve: Curves.linear,
+      );
+    }
+  }
+
+  /// 图片右划事件
+  void onRightMoveImage() async {
+    if (!isRightCanScroll) {
+      _currentIndex++;
+      await scrollController.animateTo(
+        (160 * _currentIndex).toDouble(),
+        duration: const Duration(milliseconds: 300),
+        curve: Curves.linear,
+      );
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return FContainer(
+      height: 120,
+      margin: const EdgeInsets.symmetric(
+        vertical: 15,
+      ),
+      child: FRow(
+        children: [
+          _buildImageListIcon(
+            FIcons.fis_left_arrow,
+            isLeftCanScroll,
+            onLeftMoveImage,
+          ),
+          FExpanded(
+            child: LeftSiderImageList(
+              scrollController: scrollController,
+              globalKey: globalKey,
+              remedicalList: measureData.remedicalList,
+            ),
+          ),
+          _buildImageListIcon(
+            FIcons.fis_right_arrow,
+            isRightCanScroll,
+            onRightMoveImage,
+          ),
+        ],
+      ),
+    );
+  }
+
+  @override
+  void initState() {
+    scrollController.addListener(
+      () async {
+        // 如果滑动到最右边
+        if (scrollController.position.pixels ==
+            scrollController.position.maxScrollExtent) {
+          setState(() {
+            _isEnd = true;
+          });
+        } else {
+          setState(
+            () {
+              _isEnd = false;
+            },
+          );
+        }
+      },
+    );
+    super.initState();
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    WidgetsBinding.instance?.addPostFrameCallback(
+      (mag) {
+        width = globalKey.currentContext!.size!.width;
+        setState(
+          () {
+            _isEnd = width >= measureData.remedicalList.length * 160;
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  FWidget _buildImageListIcon(
+      IconData iconsData, bool isCanClick, Function onIconTap) {
+    return FInkWell(
+      onTap: () => onIconTap.call(),
+      child: FContainer(
+        width: 100,
+        child: FIcon(
+          iconsData,
+          color: isCanClick ? Colors.grey : Colors.white,
+          size: 30,
+        ),
+      ),
+    );
+  }
+}
+
 /// 当前测量组的所有图片
 class LeftSiderImageList extends StatefulWidget implements FWidget {
   LeftSiderImageList({
@@ -31,27 +179,29 @@ class _LeftSiderImageListState extends State<LeftSiderImageList> {
   /// 测量AI数据
   final measureData = Get.find<MeasureDataController>();
 
-  /// 测量主页面的控制器
-  // final controller = Get.find<MeasureHomeController>();
+  late final measureController = Get.find<MeasureController>();
+  late final measureHandler = Get.put(MeasureHandler());
 
   int selectedImageIndex = -1;
 
-  // /// 获取图片地址
-  // void onChangeImage(
-  //   String imageUrl,
-  //   String remedicalCode,
-  // ) async {
-  //   await controller.findRemedicalByCodeAsync(remedicalCode);
-  //   controller.state.loaded = false;
-
-  //   ExamImageInfo selectedImage = controller.examImageInfoList.firstWhere(
-  //     (element) => element.url == imageUrl,
-  //   );
-  //   selectedImageIndex = controller.examImageInfoList.indexOf(selectedImage);
-  //   measureCurrentImage.itemCurrentImage = imageUrl;
-  //   controller.measureController.examInfo.selectedImageIndex =
-  //       selectedImageIndex;
-  // }
+  /// 获取图片地址
+  void onChangeImage(
+    String imageUrl,
+    String remedicalCode,
+  ) async {
+    measureHandler.imageChanged = ChangeImageInfo(
+      imageUrl,
+      remedicalCode,
+    );
+    ExamImageInfo selectedImage = measureController.examInfo.images.firstWhere(
+      (element) => element.url == imageUrl,
+    );
+    selectedImageIndex =
+        measureController.examInfo.images.indexOf(selectedImage);
+    measureData.itemCurrentImage = imageUrl;
+    measureController.examInfo.selectedImageIndex = selectedImageIndex;
+    setState(() {});
+  }
 
   @override
   void initState() {
@@ -66,6 +216,7 @@ class _LeftSiderImageListState extends State<LeftSiderImageList> {
   @override
   FWidget build(BuildContext context) {
     return FContainer(
+      key: ValueKey(measureData.itemCurrentImage),
       alignment: Alignment.topCenter,
       child: _buildImageList(widget.remedicalList),
     );
@@ -88,12 +239,13 @@ class _LeftSiderImageListState extends State<LeftSiderImageList> {
             diagnosisConclusion: remedicalItemList[index].diagnosisConclusion,
             diagnosisOrgans: remedicalItemList[index].diagnosisOrgans,
             isMeasure: true,
-            // onChangeImage: () => onChangeImage(
-            //   remedicalItemList[index].terminalImages!.imageUrl!,
-            //   remedicalItemList[index].remedicalCode!,
-            // ),
+            onChangeImage: () => onChangeImage(
+              remedicalItemList[index].terminalImages!.imageUrl!,
+              remedicalItemList[index].remedicalCode!,
+            ),
           );
           return FContainer(
+            key: ValueKey(measureData.itemCurrentImage),
             width: 160,
             alignment: Alignment.center,
             decoration: BoxDecoration(

+ 14 - 158
lib/view/measure/measure_player.dart

@@ -1,6 +1,5 @@
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
 import 'package:fis_measure/view/main/desktop.dart';
-import 'package:fis_measure/view/measure/measure_images_bar.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
@@ -12,176 +11,33 @@ class MeasureRightBoard extends StatefulWidget implements FWidget {
 }
 
 class _MeasureRightBoardState extends State<MeasureRightBoard> {
-  /// 图片滑动控制器
-  ScrollController scrollController = ScrollController();
-  // final controller = Get.find<MeasureHomeController>();
   /// 数据
   final measureData = Get.find<MeasureDataController>();
 
-  // /// 当前测量的图片
-  // final measureCurrentImage = Get.put(MeasureGetCurrentImage());
-
-  // /// 测量组图片
-  // final remedicalList = Get.find<MeasureGetRemedicalListResult>();
-
-  /// 当前可滑动的范围宽度
-  late double width = 0;
-
-  /// 是否滑到最后
-  bool _isEnd = false;
-
-  /// 计算下标
-  int _currentIndex = 0;
-
-  /// 左边能否滑动
-  bool get isLeftCanScroll => _currentIndex <= 0;
-
-  /// 右边能否滑动
-  bool get isRightCanScroll => _isEnd;
-
-  /// 滑动区域的key值
-  final GlobalKey globalKey = GlobalKey();
-
-  /// 图片左划事件
-  void onLeftMoveImage() async {
-    if (_currentIndex > 0) {
-      _currentIndex--;
-      await scrollController.animateTo(
-        (160 * _currentIndex).toDouble(),
-        duration: const Duration(milliseconds: 300),
-        curve: Curves.linear,
-      );
-    }
-  }
-
-  /// 图片右划事件
-  void onRightMoveImage() async {
-    if (!isRightCanScroll) {
-      _currentIndex++;
-      await scrollController.animateTo(
-        (160 * _currentIndex).toDouble(),
-        duration: const Duration(milliseconds: 300),
-        curve: Curves.linear,
-      );
-    }
-  }
-
   @override
   FWidget build(BuildContext context) {
     return FContainer(
       padding: const EdgeInsets.all(8).copyWith(left: 0),
-      child: FColumn(
-        mainAxisSize: MainAxisSize.max,
+      child: FStack(
         children: [
-          FExpanded(
+          MeasureMainView(
             key: ValueKey(measureData.itemCurrentImage),
-            child: FStack(
-              children: [
-                const MeasureMainView(),
-                FPositioned(
-                  right: 10,
-                  top: 10,
-                  child: FIconButton(
-                    onPressed: () {
-                      Get.back();
-                      // controller.dispose();
-                    },
-                    icon: const FIcon(
-                      Icons.close,
-                      color: Colors.white,
-                    ),
-                  ),
-                )
-              ],
-            ),
           ),
-          FContainer(
-            height: 120,
-            margin: const EdgeInsets.symmetric(
-              vertical: 15,
-            ),
-            child: FRow(
-              children: [
-                _buildImageListIcon(
-                  FIcons.fis_left_arrow,
-                  isLeftCanScroll,
-                  onLeftMoveImage,
-                ),
-                FExpanded(
-                  child: LeftSiderImageList(
-                    scrollController: scrollController,
-                    globalKey: globalKey,
-                    remedicalList: measureData.remedicalList,
-                  ),
-                ),
-                _buildImageListIcon(
-                  FIcons.fis_right_arrow,
-                  isRightCanScroll,
-                  onRightMoveImage,
-                ),
-              ],
+          FPositioned(
+            right: 10,
+            top: 10,
+            child: FIconButton(
+              onPressed: () {
+                Get.back();
+              },
+              icon: const FIcon(
+                Icons.close,
+                color: Colors.white,
+              ),
             ),
-          ),
+          )
         ],
       ),
     );
   }
-
-  @override
-  void initState() {
-    scrollController.addListener(
-      () async {
-        // 如果滑动到最右边
-        if (scrollController.position.pixels ==
-            scrollController.position.maxScrollExtent) {
-          setState(() {
-            _isEnd = true;
-          });
-        } else {
-          setState(
-            () {
-              _isEnd = false;
-            },
-          );
-        }
-      },
-    );
-    super.initState();
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    WidgetsBinding.instance?.addPostFrameCallback(
-      (mag) {
-        width = globalKey.currentContext!.size!.width;
-        print(width);
-        setState(
-          () {
-            _isEnd = width >= measureData.remedicalList.length * 160;
-          },
-        );
-      },
-    );
-  }
-
-  @override
-  void dispose() {
-    super.dispose();
-  }
-
-  FWidget _buildImageListIcon(
-      IconData iconsData, bool isCanClick, Function onIconTap) {
-    return FInkWell(
-      onTap: () => onIconTap.call(),
-      child: FContainer(
-        width: 100,
-        child: FIcon(
-          iconsData,
-          color: isCanClick ? Colors.grey : Colors.white,
-          size: 30,
-        ),
-      ),
-    );
-  }
 }

+ 24 - 16
lib/view/measure/measure_tool.dart

@@ -1,10 +1,14 @@
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
+import 'package:fis_measure/process/workspace/measure_data_controller.dart';
+import 'package:fis_measure/process/workspace/measure_handler.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
 /// 测量项页面
 class LeftSiderSelectMeasure extends FStatefulWidget {
+  const LeftSiderSelectMeasure({Key? key}) : super(key: key);
+
   @override
   FState<LeftSiderSelectMeasure> createState() =>
       _LeftSiderSelectMeasureState();
@@ -18,33 +22,37 @@ class _LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
 
   /// vid应用的控制器 测量模式暂时写死
   late final application = Get.find<IApplication>();
+  late final measureHandler = Get.find<MeasureHandler>();
+
+  /// 数据
+  late final measureData = Get.find<MeasureDataController>();
 
-  /// 测量主页面的控制器
-  // final controller = Get.find<MeasureHomeController>();
   String activeName = '';
 
-  void changeItem(String name) {
-    activeName = name;
-    application.switchItemByName(name);
+  void changeItem(String? name) {
+    activeName = name ?? '';
+    application.switchItemByName(activeName);
     setState(() {});
   }
 
   Future<void> getName() async {
-    // await Future.wait(
-    //   [
-    //     controller.initGetMeasureApplication(
-    //       application.applicationName,
-    //       application.categoryName,
-    //       ['Tissue'],
-    //     )
-    //   ],
-    // );
-    // changeItem(measureConfigController.state.chooseMeasureList[0]);
+    measureHandler.measureModeChanged = MeasureModeSelection(
+      application.applicationName,
+      application.categoryName,
+      ['Tissue'],
+    );
+    if (measureData.getMeasureApplicationList.isEmpty) {
+      await Future.delayed(const Duration(milliseconds: 500), () {
+        changeItem(measureData.getMeasureApplicationList[0]);
+      });
+    }
+    changeItem(measureData.getMeasureApplicationList[0]);
   }
 
   @override
   void initState() {
     getName();
+
     super.initState();
   }
 
@@ -62,7 +70,7 @@ class _LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
         crossAxisCount: 2,
         childAspectRatio: 3,
         controller: ScrollController(),
-        children: [].map((e) {
+        children: measureData.getMeasureApplicationList.map((e) {
           if (e == activeName) {
             return _buildActivedMeasure(
               e,

+ 29 - 63
lib/view/measure/measure_view.dart

@@ -2,6 +2,7 @@ import 'package:fis_jsonrpc/rpc.dart';
 import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
 import 'package:fis_measure/process/workspace/measure_controller.dart';
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
+import 'package:fis_measure/view/measure/measure_images_bar.dart';
 import 'package:fis_measure/view/measure/measure_player.dart';
 import 'package:fis_measure/view/measure/measure_tool.dart';
 import 'package:fis_measure/view/measure/tool_chest_title.dart';
@@ -10,13 +11,13 @@ import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
 /// 测量主页面
-class MeasureMainPage1 extends StatefulWidget implements FWidget {
-  const MeasureMainPage1({Key? key}) : super(key: key);
+class MeasureMainPage extends StatefulWidget implements FWidget {
+  const MeasureMainPage({Key? key}) : super(key: key);
   @override
   State<StatefulWidget> createState() => _MeasureMainPageState();
 }
 
-class _MeasureMainPageState extends State<MeasureMainPage1> {
+class _MeasureMainPageState extends State<MeasureMainPage> {
   /// 数据
   final measureData = Get.find<MeasureDataController>();
 
@@ -24,27 +25,14 @@ class _MeasureMainPageState extends State<MeasureMainPage1> {
   List<ExamImageInfo> examImageInfoList = [];
 
   /// 测量控制器
-  late MeasureController measureController;
+  late MeasureController measureController = Get.put(MeasureController(
+    "",
+    imagesFetchFunc: (code) async {
+      return examImageInfoList;
+    },
+  ));
   bool loaded = false;
 
-  // /// 测量主页面的控制器
-  // final measurehomeController = Get.put(MeasureHomeController());
-
-  // /// 测量图片组控制器
-  // final remedicalList = Get.put(MeasureGetRemedicalListResult());
-
-  // /// 当前测量的图片
-  // MeasureGetCurrentImage measureCurrentImage =
-  //     Get.put(MeasureGetCurrentImage());
-
-  // /// 图片loadding事件
-  // void onImageLoaded(Object sender, ExamImageInfo? e) {
-  //   if (!mounted) return;
-  //   if (e != null) {
-  //     measurehomeController.state.loaded = true;
-  //   }
-  // }
-
   /// 获取测量图片所需的图片组 并且写入控制器中 加载
   void getExamImageInfoList(List<RemedicalInfoDTO> remedicals) {
     for (var element in remedicals) {
@@ -55,12 +43,13 @@ class _MeasureMainPageState extends State<MeasureMainPage1> {
         ),
       );
     }
-    measureController = MeasureController(
-      '',
+
+    measureController = Get.put(MeasureController(
+      "",
       imagesFetchFunc: (code) async {
         return examImageInfoList;
       },
-    );
+    ));
     measureController.load().then((value) {
       int selectedImageIndex = -1;
       ExamImageInfo selectedImage = examImageInfoList.firstWhere(
@@ -74,34 +63,8 @@ class _MeasureMainPageState extends State<MeasureMainPage1> {
   @override
   void initState() {
     List<RemedicalInfoDTO> remedicals = measureData.remedicalList;
-    print(remedicals);
-    // List<RemedicalInfoDTO> remedicals = remedicalList.remedicalList;
-    // final parameters = router.getParams() ?? {};
-    // print(parameters);
-    // if (parameters.keys.contains('token')) {
-    //   Store.user.setToken(parameters['token']!.toString());
-    //   String patientCode = parameters['patientCode'].toString();
-    //   String currentImage = parameters['index'] ?? '';
-    //   int imageInedex = int.parse(parameters['imageInedex'] ?? '0');
-    //   measureCurrentImage.itemCurrentImage = currentImage;
-    //   measurehomeController
-    //       .getRemedicalList(patientCode, imageInedex)
-    //       .then((value) {
-    //     remedicals = value;
-    //     remedicalList.remedicalList = value;
-    //     measureCurrentImage = measurehomeController.getMeasureCurrentImage(
-    //         remedicals[imageInedex].terminalImages!.imageUrl ?? '');
-    //     getExamImageInfoList(remedicals);
-    //     WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
-    //       measurehomeController.measureController.imageLoaded
-    //           .addListener(onImageLoaded);
-    //     });
-    //   });
-    // } else {
     getExamImageInfoList(remedicals);
-    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
-      measureController.imageLoaded.addListener(onImageLoaded);
-    });
+    measureController.imageLoaded.addListener(onImageLoaded);
 
     super.initState();
   }
@@ -117,18 +80,12 @@ class _MeasureMainPageState extends State<MeasureMainPage1> {
 
   @override
   void dispose() {
-    // measurehomeController.controller.imageLoaded.removeListener(onImageLoaded);
-    // Get.delete<IMeasureController>();
-    // Get.delete<IPlayerController>();
-    // measurehomeController.dispose();
     super.dispose();
   }
 
   @override
   FWidget build(BuildContext context) {
     FWidget body;
-
-    //  = FObx(() {
     if (!loaded) {
       const loadingWidget = FCenter(child: FCircularProgressIndicator());
       body = FRow(
@@ -143,13 +100,22 @@ class _MeasureMainPageState extends State<MeasureMainPage1> {
         children: [
           _MeasureLeftBoard(),
           const FVerticalDivider(),
-          const FExpanded(
-            child: MeasureRightBoard(),
+          FExpanded(
+            child: FColumn(
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                FExpanded(
+                  child: MeasureRightBoard(
+                    key: UniqueKey(),
+                  ),
+                ),
+                const MeasureImagesBar(),
+              ],
+            ),
           ),
         ],
       );
     }
-    // });
 
     return FCenter(
       child: FContainer(
@@ -168,12 +134,12 @@ class _MeasureLeftBoard extends FStatelessWidget {
       mainAxisSize: MainAxisSize.max,
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
-        LeftSiderHold(),
+        const LeftSiderHold(),
         FExpanded(
           child: FContainer(
             width: 300,
             key: UniqueKey(),
-            child: LeftSiderSelectMeasure(),
+            child: const LeftSiderSelectMeasure(),
           ),
         ),
       ],

+ 4 - 3
lib/view/measure/tool_chest_title.dart

@@ -2,9 +2,6 @@ import 'package:fis_i18n/i18n.dart';
 import 'package:fis_theme/theme.dart';
 import 'package:fis_ui/index.dart';
 import 'package:flutter/material.dart';
-// import 'package:flyinsono/architecture/router/router.dart';
-// import 'package:flyinsono/pages/measure/measure_configuration/view.dart';
-// import 'package:flyinsono/pages/measure/measure_configuration/widgets/measure_configuration_style.dart';
 
 /// 下拉选择的类
 class MeasureSelectModel {
@@ -15,6 +12,8 @@ class MeasureSelectModel {
 
 /// 工具箱 样式配置之类
 class LeftSiderHold extends StatelessWidget implements FWidget {
+  const LeftSiderHold({Key? key}) : super(key: key);
+
   @override
   FWidget build(BuildContext context) {
     return FContainer(
@@ -25,6 +24,8 @@ class LeftSiderHold extends StatelessWidget implements FWidget {
       ),
       child: FColumn(
         key: UniqueKey(),
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
         children: [
           FRow(
             mainAxisAlignment: MainAxisAlignment.spaceBetween,

+ 174 - 207
lib/view/paint/ai_patint.dart

@@ -1,11 +1,13 @@
 import 'dart:convert';
-import 'dart:ui';
 
 import 'package:fis_measure/interfaces/date_types/int_size.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
-import 'package:fis_measure/utils/canvas.dart';
+import 'package:fis_measure/view/paint/date_structure.dart';
+import 'package:fis_measure/view/paint/parts/dash_line.dart';
+import 'package:fis_measure/view/paint/parts/dots.dart';
+import 'package:fis_measure/view/paint/parts/rectangle.dart';
 import 'package:fis_measure/view/player/controller.dart';
 import 'package:fis_measure/view/player/enums.dart';
 import 'package:fis_measure/view/player/events.dart';
@@ -13,6 +15,11 @@ import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 
 class AIPaintInfo extends StatefulWidget {
+  /// 播放器控制器
+  final VidPlayerController controller;
+  final double? width;
+  final double? height;
+
   const AIPaintInfo(
     this.controller, {
     Key? key,
@@ -20,11 +27,6 @@ class AIPaintInfo extends StatefulWidget {
     this.height,
   }) : super(key: key);
 
-  final VidPlayerController controller;
-
-  final double? width;
-  final double? height;
-
   @override
   State<AIPaintInfo> createState() => _AIPaintInfoState();
 }
@@ -34,28 +36,42 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
 
   late final application = Get.find<IApplication>();
 
+  /// ai结果
+  late final List<AIDiagnosisPerImageDTO> aiResult = [];
+
+  /// ai json 数据
+  String? aiResults;
+
   /// 测量AI数据
   final measureData = Get.find<MeasureDataController>();
+
+  /// 播放控制器
   final playerController = Get.find<IPlayerController>();
+
+  /// ai结果图展示
   late List<Offset> aiResultsList = [];
+
+  /// ai的横纵坐标 四个点
   late Offset p1 = Offset.zero;
   late Offset p2 = Offset.zero;
   late Offset p3 = Offset.zero;
   late Offset p4 = Offset.zero;
 
+  /// 视频时的画面
   double left = -1;
   double top = -1;
   double width = -1;
   double height = -1;
 
+  /// 当前帧数
   int? frameIndex;
 
-  @override
-  void didUpdateWidget(covariant AIPaintInfo oldWidget) {
-    if (oldWidget.controller != widget.controller) {
-      throw UnsupportedError("[VidTestPlayer] unsupport replace controller.");
+  void onMeasuredAIResultsInfoChanged(Object sender, String e) {
+    if (e.isNotEmpty) {
+      aiResults = e;
+
+      setState(() {});
     }
-    super.didUpdateWidget(oldWidget);
   }
 
   void onControllerEvent(Object sender, VidPlayerEvent e) {
@@ -85,100 +101,93 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
     setState(() {});
   }
 
-  bool onGetAIResultsInfo(double widthScale) {
-    final aiResult = jsonDecode(measureData.aiResults);
-    if ((aiResult as List).isEmpty) {
-      return false;
-    } else if (aiResult.length == 1) {
-      final diagResultsForEachOrgan = aiResult[0]['DiagResultsForEachOrgan'][0];
-      final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
-      if ((detectedObjects as List).isNotEmpty) {
-        for (int m = 0; m < detectedObjects.length; m++) {
-          final contours = detectedObjects[m]['Contours'] as List;
-          final descriptions = jsonDecode(detectedObjects[m]['Descriptions']
-                  [(detectedObjects[m]['Descriptions'] as List).length - 1]
-              ['Value']);
-          if (_HAS_VIEW_STATUS_ARR
-              .contains((playerController as VidPlayerController).status)) {
-            for (int i = 0; i < contours.length; i++) {
-              if (i % 10 == 0) {
-                aiResultsList.add(Offset(
-                  contours[i]['X'] * widthScale,
-                  contours[i]['Y'] * widthScale,
-                ));
-              }
+  /// 单帧图处理
+  void onSingleFrameImage(
+      List<AIDetectedObject>? detectedObjects, double widthScale) {
+    for (int m = 0; m < detectedObjects!.length; m++) {
+      final List<AIDiagnosisPoint2D>? contours = detectedObjects[m].contours;
+      if (contours!.isNotEmpty && detectedObjects[m].descriptions!.isNotEmpty) {
+        final descriptions = jsonDecode(
+          detectedObjects[m]
+              .descriptions![detectedObjects[m].descriptions!.length - 1]
+              .value!,
+        );
+        if (_HAS_VIEW_STATUS_ARR
+            .contains((playerController as VidPlayerController).status)) {
+          for (int i = 0; i < contours.length; i++) {
+            if (i % 10 == 0) {
+              aiResultsList.add(Offset(
+                contours[i].x * widthScale,
+                contours[i].y * widthScale,
+              ));
             }
-            p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
-                descriptions['HorizontalPoint1']['Y'] * widthScale);
-            p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
-                descriptions['HorizontalPoint2']['Y'] * widthScale);
-            p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
-                descriptions['VerticalPoint1']['Y'] * widthScale);
-            p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
-                descriptions['VerticalPoint2']['Y'] * widthScale);
           }
+          p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
+              descriptions['HorizontalPoint1']['Y'] * widthScale);
+          p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
+              descriptions['HorizontalPoint2']['Y'] * widthScale);
+          p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
+              descriptions['VerticalPoint1']['Y'] * widthScale);
+          p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
+              descriptions['VerticalPoint2']['Y'] * widthScale);
         }
+      }
+    }
+  }
+
+  /// ai处理图片结果
+  bool onGetAIResultsInfo(double widthScale) {
+    if (aiResult.isEmpty) {
+      return false;
+    } else if (aiResult.length == 1) {
+      final diagResultsForEachOrgan = aiResult[0].diagResultsForEachOrgan?[0];
+      final detectedObjects = diagResultsForEachOrgan!.detectedObjects;
+      if (detectedObjects!.isNotEmpty) {
+        onSingleFrameImage(
+          detectedObjects,
+          widthScale,
+        );
       } else {
         return false;
       }
       return true;
     } else {
       _updateFeatures();
+      final diagResultsForEachOrgan =
+          aiResult[frameIndex!].diagResultsForEachOrgan![0];
+
+      final detectedObjects = diagResultsForEachOrgan.detectedObjects;
       if ((playerController as VidPlayerController).status ==
           VidPlayStatus.pause) {
-        final diagResultsForEachOrgan =
-            aiResult[frameIndex!]['DiagResultsForEachOrgan'][0];
-
-        final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
-        for (int m = 0; m < (detectedObjects as List).length; m++) {
-          final contours = detectedObjects[m]['Contours'] as List;
-          if (contours.isEmpty) {
-            return false;
-          } else {
-            if ((detectedObjects[m]['Descriptions'] as List).isNotEmpty) {
-              final descriptions = jsonDecode(detectedObjects[m]['Descriptions']
-                      [(detectedObjects[m]['Descriptions'] as List).length - 1]
-                  ['Value']);
-              if (_HAS_VIEW_STATUS_ARR
-                  .contains((playerController as VidPlayerController).status)) {
-                for (int i = 0; i < contours.length; i++) {
-                  if (i % 10 == 0) {
-                    aiResultsList.add(Offset(
-                      contours[i]['X'] * widthScale,
-                      contours[i]['Y'] * widthScale,
-                    ));
-                  }
-                }
-                p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
-                    descriptions['HorizontalPoint1']['Y'] * widthScale);
-                p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
-                    descriptions['HorizontalPoint2']['Y'] * widthScale);
-                p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
-                    descriptions['VerticalPoint1']['Y'] * widthScale);
-                p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
-                    descriptions['VerticalPoint2']['Y'] * widthScale);
-              }
-            }
-          }
+        if (detectedObjects!.isNotEmpty) {
+          onSingleFrameImage(
+            detectedObjects,
+            widthScale,
+          );
         }
       } else if (playerController.status == VidPlayStatus.play) {
-        final diagResultsForEachOrgan =
-            aiResult[frameIndex!]['DiagResultsForEachOrgan'][0];
-        final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
-
-        for (int m = 0; m < (detectedObjects as List).length; m++) {
-          final organBoundBox = detectedObjects[m]['BoundingBox'];
+        for (int m = 0; m < detectedObjects!.length; m++) {
+          final organBoundBox = detectedObjects[m].boundingBox;
 
-          left = organBoundBox['Left'] * widthScale;
-          top = organBoundBox['Top'] * widthScale;
-          width = organBoundBox['Width'] * widthScale;
-          height = organBoundBox['Height'] * widthScale;
+          left = organBoundBox!.left * widthScale;
+          top = organBoundBox.top * widthScale;
+          width = organBoundBox.width * widthScale;
+          height = organBoundBox.height * widthScale;
         }
       }
       return true;
     }
   }
 
+  @override
+  void didUpdateWidget(covariant AIPaintInfo oldWidget) {
+    if (oldWidget.controller != widget.controller) {
+      throw UnsupportedError("[VidTestPlayer] unsupport replace controller.");
+    }
+
+    super.didUpdateWidget(oldWidget);
+  }
+
   @override
   void initState() {
     widget.controller.eventHandler.addListener(onControllerEvent);
@@ -196,44 +205,71 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
 
   @override
   Widget build(BuildContext context) {
-    return LayoutBuilder(
-      builder: (context, constraints) {
-        final canvasWidth = constraints.maxWidth;
-        late final frameDataWidth = application.frameData?.width;
-        late final widthScale = (canvasWidth / frameDataWidth!);
-        if (!onGetAIResultsInfo(widthScale)) {
-          return const SizedBox();
-        } else {
-          return CustomMultiChildLayout(
-            delegate: _LayerLayoutDelegate(),
-            children: [
-              LayoutId(
-                id: _LayerLayoutIds.dots,
-                child: CustomPaint(
-                  painter: PaintAIDots(aiResultsList),
+    Widget? child;
+
+    switch (widget.controller.status) {
+      case VidPlayStatus.init:
+        child = Container(child: const Text("Loading"));
+        break;
+      case VidPlayStatus.ready:
+        child = Container(child: const Text("Ready"));
+        break;
+      case VidPlayStatus.loadFail:
+        child = Container(child: const Text("Load fail"));
+        break;
+      case VidPlayStatus.play:
+      case VidPlayStatus.pause:
+        child = LayoutBuilder(builder: (context, constraints) {
+          final canvasWidth = constraints.maxWidth;
+          late final frameDataWidth = application.frameData?.width;
+          late final widthScale = (canvasWidth / frameDataWidth!);
+          if (!onGetAIResultsInfo(widthScale)) {
+            return const SizedBox();
+          } else {
+            return CustomMultiChildLayout(
+              key: UniqueKey(),
+              delegate: _LayerLayoutDelegate(),
+              children: [
+                LayoutId(
+                  id: _LayerLayoutIds.dots,
+                  child: CustomPaint(
+                    painter: PaintAIDots(aiResultsList),
+                  ),
                 ),
-              ),
-              LayoutId(
-                id: _LayerLayoutIds.dashLine,
-                child: CustomPaint(
-                  painter: PaintAIDashLine(p1, p2, p3, p4),
+                LayoutId(
+                  id: _LayerLayoutIds.dashLine,
+                  child: CustomPaint(
+                    painter: PaintAIDashLine(p1, p2, p3, p4),
+                  ),
                 ),
-              ),
-              LayoutId(
-                id: _LayerLayoutIds.rectangle,
-                child: CustomPaint(
-                  painter: PaintAIRectangle(
-                    left,
-                    top,
-                    width,
-                    height,
+                LayoutId(
+                  id: _LayerLayoutIds.rectangle,
+                  child: CustomPaint(
+                    painter: PaintAIRectangle(
+                      left,
+                      top,
+                      width,
+                      height,
+                    ),
                   ),
                 ),
-              ),
-            ],
-          );
-        }
-      },
+              ],
+            );
+          }
+        });
+        break;
+      case VidPlayStatus.stop:
+      case VidPlayStatus.dispose:
+        child = Container(child: const Text("Closed"));
+        break;
+    }
+    return buildBox(context, child);
+  }
+
+  Widget buildBox(BuildContext context, Widget child) {
+    return Container(
+      alignment: Alignment.center,
+      child: child,
     );
   }
 
@@ -257,12 +293,29 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
     }
   }
 
-  void _addListenrs() {
+  void _addListenrs() async {
     application.updateRenderReady.addListener(_onMeasureRerenderReady);
+    measureData.aiResultsInfoChanged
+        .addListener(onMeasuredAIResultsInfoChanged);
+    await Future.delayed(const Duration(milliseconds: 300), () {
+      final measureDataAIResults = jsonDecode(
+        (aiResults ?? '').isEmpty ? measureData.aiResults : aiResults ?? '',
+      );
+      for (int i = 0; i < (measureDataAIResults as List).length; i++) {
+        aiResult.add(
+          AIDiagnosisPerImageDTO.fromJson(
+            measureDataAIResults[i],
+          ),
+        );
+      }
+      setState(() {});
+    });
   }
 
   void _removeListenrs() {
     application.updateRenderReady.removeListener(_onMeasureRerenderReady);
+    measureData.aiResultsInfoChanged
+        .removeListener(onMeasuredAIResultsInfoChanged);
   }
 }
 
@@ -274,8 +327,6 @@ class _LayerLayoutDelegate extends MultiChildLayoutDelegate {
 
   @override
   void performLayout(Size size) {
-    // if (!hasChild(_LayerLayoutIds.player)) return;
-
     final application = Get.find<IApplication>();
     final vidFrame = application.frameData;
     final imageSize = IntSize.fill(vidFrame?.width ?? 0, vidFrame?.height ?? 0);
@@ -335,90 +386,6 @@ class _LayerLayoutDelegate extends MultiChildLayoutDelegate {
   }
 }
 
-Paint _paint = Paint()
-  ..color = const Color.fromARGB(255, 255, 255, 0)
-  ..isAntiAlias = true
-  ..strokeWidth = 2
-  ..style = PaintingStyle.stroke;
-
-/// ai的虚线部分
-class PaintAIDashLine extends CustomPainter {
-  final Offset p1;
-  final Offset p2;
-  final Offset p3;
-  final Offset p4;
-  PaintAIDashLine(
-    this.p1,
-    this.p2,
-    this.p3,
-    this.p4,
-  );
-  @override
-  void paint(Canvas canvas, Size size) {
-    canvas.drawDashLine(p1, p2, 2, 5, _paint);
-    canvas.drawDashLine(p3, p4, 2, 5, _paint);
-    canvas.drawVertex(p1, 10);
-    canvas.drawVertex(p2, 10);
-    canvas.drawVertex(p3, 10);
-    canvas.drawVertex(p4, 10);
-  }
-
-  @override
-  bool shouldRepaint(CustomPainter oldDelegate) {
-    return false;
-  }
-}
-
-/// ai描点
-class PaintAIDots extends CustomPainter {
-  final List<Offset>? aiResultsList;
-  PaintAIDots(this.aiResultsList);
-  @override
-  void paint(Canvas canvas, Size size) {
-    canvas.drawPoints(
-      ///PointMode的枚举类型有三个,points(点),lines(线,隔点连接),polygon(线,相邻连接)
-      PointMode.points,
-      aiResultsList ?? [],
-      _paint,
-    );
-  }
-
-  @override
-  bool shouldRepaint(covariant PaintAIDots oldDelegate) {
-    return oldDelegate.hashCode != hashCode;
-  }
-}
-
-/// ai矩形
-class PaintAIRectangle extends CustomPainter {
-  final double left;
-  final double top;
-  final double width;
-  final double height;
-
-  PaintAIRectangle(
-    this.left,
-    this.top,
-    this.width,
-    this.height,
-  );
-  @override
-  void paint(Canvas canvas, Size size) {
-    Rect rect = Rect.fromLTWH(
-      left,
-      top,
-      width,
-      height,
-    );
-    canvas.drawRect(rect, _paint);
-  }
-
-  @override
-  bool shouldRepaint(covariant PaintAIRectangle oldDelegate) {
-    return oldDelegate.hashCode != hashCode;
-  }
-}
-
 enum _LayerLayoutIds {
   /// 
   dots,

+ 246 - 0
lib/view/paint/date_structure.dart

@@ -0,0 +1,246 @@
+enum DiagnosisOrganEnum {
+  Null,
+  placeHolder_1,
+  Breast,
+  Abdomen,
+  Liver,
+  Cholecyst,
+  Kidney,
+  Spleen,
+  CarotidArtery,
+}
+
+class AIDiagnosisRect {
+  int right;
+  int bottom;
+  int left;
+  int top;
+  int width;
+  int height;
+
+  AIDiagnosisRect({
+    this.right = 0,
+    this.bottom = 0,
+    this.left = 0,
+    this.top = 0,
+    this.width = 0,
+    this.height = 0,
+  });
+
+  factory AIDiagnosisRect.fromJson(Map<String, dynamic> map) {
+    return AIDiagnosisRect(
+      right: map['Right'],
+      bottom: map['Bottom'],
+      left: map['Left'],
+      top: map['Top'],
+      width: map['Width'],
+      height: map['Height'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['Right'] = right;
+    map['Bottom'] = bottom;
+    map['Left'] = left;
+    map['Top'] = top;
+    map['Width'] = width;
+    map['Height'] = height;
+    return map;
+  }
+}
+
+class AIDiagnosisPoint2D {
+  int x;
+  int y;
+
+  AIDiagnosisPoint2D({
+    this.x = 0,
+    this.y = 0,
+  });
+
+  factory AIDiagnosisPoint2D.fromJson(Map<String, dynamic> map) {
+    return AIDiagnosisPoint2D(
+      x: map['X'],
+      y: map['Y'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['X'] = x;
+    map['Y'] = y;
+    return map;
+  }
+}
+
+enum DiagnosisDescriptionEnum {
+  Shape,
+  Orientation,
+  EchoPattern,
+  LesionBoundary,
+  Margin,
+  Calcification,
+  LesionSize,
+}
+
+class AIDiagnosisDescription {
+  DiagnosisDescriptionEnum type;
+  String? value;
+
+  AIDiagnosisDescription({
+    this.type = DiagnosisDescriptionEnum.Shape,
+    this.value,
+  });
+
+  factory AIDiagnosisDescription.fromJson(Map<String, dynamic> map) {
+    return AIDiagnosisDescription(
+      type: DiagnosisDescriptionEnum.values
+          .firstWhere((e) => e.index == map['Type']),
+      value: map['Value'],
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['Type'] = type.index;
+    if (value != null) map['Value'] = value;
+    return map;
+  }
+}
+
+class AIDetectedObject {
+  int label;
+  double confidence;
+  AIDiagnosisRect? boundingBox;
+  List<AIDiagnosisPoint2D>? contours;
+  List<AIDiagnosisDescription>? descriptions;
+
+  AIDetectedObject({
+    this.label = 0,
+    this.confidence = 0,
+    this.boundingBox,
+    this.contours,
+    this.descriptions,
+  });
+
+  factory AIDetectedObject.fromJson(Map<String, dynamic> map) {
+    return AIDetectedObject(
+      label: map['Label'],
+      confidence: map['Confidence'],
+      boundingBox: map['BoundingBox'] != null
+          ? AIDiagnosisRect.fromJson(map['BoundingBox'])
+          : null,
+      contours: map['Contours'] != null
+          ? (map['Contours'] as List)
+              .map(
+                  (e) => AIDiagnosisPoint2D.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+      descriptions: map['Descriptions'] != null
+          ? (map['Descriptions'] as List)
+              .map((e) =>
+                  AIDiagnosisDescription.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['Label'] = label;
+    map['Confidence'] = confidence;
+    if (boundingBox != null) map['BoundingBox'] = boundingBox;
+    if (contours != null) map['Contours'] = contours;
+    if (descriptions != null) map['Descriptions'] = descriptions;
+    return map;
+  }
+}
+
+class AIDiagnosisResultPerOrgan {
+  DiagnosisOrganEnum organ;
+  AIDiagnosisRect? organBoundBox;
+  List<AIDiagnosisPoint2D>? organContours;
+  List<AIDiagnosisDescription>? organDescriptions;
+  List<AIDetectedObject>? detectedObjects;
+
+  AIDiagnosisResultPerOrgan({
+    this.organ = DiagnosisOrganEnum.Null,
+    this.organBoundBox,
+    this.organContours,
+    this.organDescriptions,
+    this.detectedObjects,
+  });
+
+  factory AIDiagnosisResultPerOrgan.fromJson(Map<String, dynamic> map) {
+    return AIDiagnosisResultPerOrgan(
+      organ:
+          DiagnosisOrganEnum.values.firstWhere((e) => e.index == map['Organ']),
+      organBoundBox: map['OrganBoundBox'] != null
+          ? AIDiagnosisRect.fromJson(map['OrganBoundBox'])
+          : null,
+      organContours: map['OrganContours'] != null
+          ? (map['OrganContours'] as List)
+              .map(
+                  (e) => AIDiagnosisPoint2D.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+      organDescriptions: map['OrganDescriptions'] != null
+          ? (map['OrganDescriptions'] as List)
+              .map((e) =>
+                  AIDiagnosisDescription.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+      detectedObjects: map['DetectedObjects'] != null
+          ? (map['DetectedObjects'] as List)
+              .map((e) => AIDetectedObject.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['Organ'] = organ.index;
+    if (organBoundBox != null) map['OrganBoundBox'] = organBoundBox;
+    if (organContours != null) map['OrganContours'] = organContours;
+    if (organDescriptions != null) map['OrganDescriptions'] = organDescriptions;
+    if (detectedObjects != null) map['DetectedObjects'] = detectedObjects;
+    return map;
+  }
+}
+
+class AIDiagnosisPerImageDTO {
+  int index;
+  double priorityScore;
+  List<AIDiagnosisResultPerOrgan>? diagResultsForEachOrgan;
+
+  AIDiagnosisPerImageDTO({
+    this.index = 0,
+    this.priorityScore = 0,
+    this.diagResultsForEachOrgan,
+  });
+
+  factory AIDiagnosisPerImageDTO.fromJson(Map<String, dynamic> map) {
+    return AIDiagnosisPerImageDTO(
+      index: map['Index'],
+      priorityScore: map['PriorityScore'],
+      diagResultsForEachOrgan: map['DiagResultsForEachOrgan'] != null
+          ? (map['DiagResultsForEachOrgan'] as List)
+              .map((e) =>
+                  AIDiagnosisResultPerOrgan.fromJson(e as Map<String, dynamic>))
+              .toList()
+          : null,
+    );
+  }
+
+  Map<String, dynamic> toJson() {
+    final map = <String, dynamic>{};
+    map['Index'] = index;
+    map['PriorityScore'] = priorityScore;
+    if (diagResultsForEachOrgan != null) {
+      map['DiagResultsForEachOrgan'] = diagResultsForEachOrgan;
+    }
+    return map;
+  }
+}

+ 31 - 0
lib/view/paint/parts/dash_line.dart

@@ -0,0 +1,31 @@
+import 'package:fis_measure/utils/canvas.dart';
+import 'package:fis_measure/view/paint/parts/index.dart';
+import 'package:flutter/material.dart';
+
+/// ai的虚线部分
+class PaintAIDashLine extends CustomPainter {
+  final Offset p1;
+  final Offset p2;
+  final Offset p3;
+  final Offset p4;
+  PaintAIDashLine(
+    this.p1,
+    this.p2,
+    this.p3,
+    this.p4,
+  );
+  @override
+  void paint(Canvas canvas, Size size) {
+    canvas.drawDashLine(p1, p2, 2, 5, AIPaint);
+    canvas.drawDashLine(p3, p4, 2, 5, AIPaint);
+    canvas.drawVertex(p1, 10);
+    canvas.drawVertex(p2, 10);
+    canvas.drawVertex(p3, 10);
+    canvas.drawVertex(p4, 10);
+  }
+
+  @override
+  bool shouldRepaint(CustomPainter oldDelegate) {
+    return false;
+  }
+}

+ 24 - 0
lib/view/paint/parts/dots.dart

@@ -0,0 +1,24 @@
+import 'dart:ui';
+
+import 'package:fis_measure/view/paint/parts/index.dart';
+import 'package:flutter/material.dart';
+
+/// ai描点
+class PaintAIDots extends CustomPainter {
+  final List<Offset>? aiResultsList;
+  PaintAIDots(this.aiResultsList);
+  @override
+  void paint(Canvas canvas, Size size) {
+    canvas.drawPoints(
+      ///PointMode的枚举类型有三个,points(点),lines(线,隔点连接),polygon(线,相邻连接)
+      PointMode.points,
+      aiResultsList ?? [],
+      AIPaint,
+    );
+  }
+
+  @override
+  bool shouldRepaint(covariant PaintAIDots oldDelegate) {
+    return oldDelegate.hashCode != hashCode;
+  }
+}

+ 8 - 0
lib/view/paint/parts/index.dart

@@ -0,0 +1,8 @@
+import 'package:flutter/material.dart';
+
+// ignore: non_constant_identifier_names
+Paint AIPaint = Paint()
+  ..color = const Color.fromARGB(255, 255, 255, 0)
+  ..isAntiAlias = true
+  ..strokeWidth = 2
+  ..style = PaintingStyle.stroke;

+ 32 - 0
lib/view/paint/parts/rectangle.dart

@@ -0,0 +1,32 @@
+import 'package:fis_measure/view/paint/parts/index.dart';
+import 'package:flutter/material.dart';
+
+/// ai矩形
+class PaintAIRectangle extends CustomPainter {
+  final double left;
+  final double top;
+  final double width;
+  final double height;
+
+  PaintAIRectangle(
+    this.left,
+    this.top,
+    this.width,
+    this.height,
+  );
+  @override
+  void paint(Canvas canvas, Size size) {
+    Rect rect = Rect.fromLTWH(
+      left,
+      top,
+      width,
+      height,
+    );
+    canvas.drawRect(rect, AIPaint);
+  }
+
+  @override
+  bool shouldRepaint(covariant PaintAIRectangle oldDelegate) {
+    return oldDelegate.hashCode != hashCode;
+  }
+}