فهرست منبع

引入CrossFrameContext&修复context被清空bug

Melon 11 ماه پیش
والد
کامیت
d1e55e06f2

+ 11 - 0
lib/interfaces/process/workspace/application.dart

@@ -12,6 +12,7 @@ import '../modes/mode.dart';
 import '../viewports/viewport.dart';
 import '../visuals/visual.dart';
 import '../visuals/visual_area.dart';
+import 'cross_frame.dart';
 import 'point_info.dart';
 import 'recorder.dart';
 
@@ -91,6 +92,10 @@ abstract class IApplication {
   /// 显示缩放比例
   double get displayScaleRatio;
 
+  /// 跨帧上下文
+  ICrossFrameContext? get crossFrameContext;
+  set crossFrameContext(ICrossFrameContext? val);
+
   /// 当前模式变化事件
   late final FEventHandler<IMode> currentModeChanged;
 
@@ -118,6 +123,12 @@ abstract class IApplication {
   /// 显示画布尺寸变更事件
   late final FEventHandler<Size> displaySizeChanged;
 
+  /// 触发跨帧事件
+  late final FEventHandler<void> crossFrameTriggered;
+
+  /// 增加跨帧测量帧事件
+  late final FEventHandler<int> crossFrameAdded;
+
   /// 加载帧
   ///
   /// [frame] vid单帧数据

+ 43 - 0
lib/interfaces/process/workspace/cross_frame.dart

@@ -1,6 +1,49 @@
 import 'package:fis_common/event/event_type.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
 import 'package:vid/us/vid_us_image.dart';
 
+/// 跨帧测量上下文
+abstract class ICrossFrameContext {
+  /// 初始图像链接
+  String get originUrl;
+
+  /// 当前图像链接
+  String get currentUrl;
+
+  /// 初始帧索引
+  int get originIndex;
+
+  /// 当前帧索引
+  int? get currentIndex;
+
+  /// 当前帧
+  VidUsImage? get currentFrame;
+
+  /// 当前图像中 有测量记录的 帧索引 集合
+  List<int> get recordIndexList;
+
+  /// 测量项
+  IMeasureItem get measureItem;
+
+  /// 切换图像
+  ///
+  /// [url] 图像链接
+  void switchImage(String url);
+
+  /// 切换帧
+  ///
+  /// [frame] 帧数据
+  void switchFrame(VidUsImage frame);
+
+  /// 记录当前帧
+  ///
+  /// [index] 帧索引
+  void recordFrame(int index);
+
+  /// 测量结束事件
+  late final FEventHandler<void> measureOver;
+}
+
 /// 跨帧贮存器
 abstract class ICrossFrameStore {
   /// 原始帧

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

@@ -11,6 +11,7 @@ abstract class TopMeasureItem<T extends MeasureItemFeature>
     extends MeasureItem<T> implements ITopMeasureItem {
   final List<IMeasureItem> _childItems = [];
   int _childIndex = 0;
+  bool _isCrossFrameMode = false;
 
   bool get ifAutoStart => false;
   bool get ifAutoFinish => false;
@@ -38,7 +39,9 @@ abstract class TopMeasureItem<T extends MeasureItemFeature>
   bool get repeatableEditable => true;
 
   @override
-  bool get isCrossFrameMode => false;
+  bool get isCrossFrameMode => _isCrossFrameMode;
+  @protected
+  set isCrossFrameMode(bool val) => _isCrossFrameMode = val;
 
   @override
   late final FEventHandler<int> workingChildChanged;

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

@@ -23,6 +23,7 @@ class StraightLineGroup extends TopMeasureItem<StraightLineGroupFeature> {
       throw ArgumentError();
     }
     var ins = StraightLineGroup(meta);
+    ins.isCrossFrameMode = true;
     ins.calculator = LvStudyDistanceGroupCal(ins);
 
     return ins;

+ 40 - 21
lib/process/workspace/application.dart

@@ -13,6 +13,7 @@ import 'package:fis_measure/interfaces/process/viewports/viewport.dart';
 import 'package:fis_measure/interfaces/process/modes/mode.dart';
 import 'package:fis_common/event/event_type.dart';
 import 'package:fis_measure/interfaces/process/workspace/application.dart';
+import 'package:fis_measure/interfaces/process/workspace/cross_frame.dart';
 import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
 import 'package:fis_measure/interfaces/process/workspace/recorder.dart';
 import 'package:fis_measure/process/annotations/arrow_annotation.dart';
@@ -25,6 +26,7 @@ import 'package:flutter/painting.dart';
 import 'package:vid/us/vid_us_image.dart';
 import 'package:vid/us/vid_us_probe.dart';
 import 'dart:math';
+import 'cross_frame.dart';
 import 'recorder.dart';
 import 'third_part/application.dart';
 import 'visual_loader.dart';
@@ -35,7 +37,6 @@ class Application implements IApplication {
 
   late VidUsProbe _probe;
   VidUsImage? _frame;
-  VidUsImage? _subFrame;
   List<IVisual>? _visuals;
   IMeasureItem? _activeMeasureItem;
   IAnnotationItem? _activeAnnotationItem;
@@ -48,6 +49,7 @@ class Application implements IApplication {
   final List<IMeasureItem> _measureItems = [];
   final Set<IAnnotationItem> _annotationItems = {};
   late final _recorder = MeasureRecorder(this);
+  ICrossFrameContext? _crossFrameContext;
 
   final emptyItem = ItemMeta(
     "EmptyItem",
@@ -67,6 +69,8 @@ class Application implements IApplication {
     operateTypeChanged = FEventHandler<MeasureOperateType>();
     visualsLoaded = FEventHandler<void>();
     displaySizeChanged = FEventHandler<Size>();
+    crossFrameTriggered = FEventHandler<void>();
+    crossFrameAdded = FEventHandler<int>();
   }
 
   @override
@@ -107,7 +111,7 @@ class Application implements IApplication {
   IVisualArea get currentVisualArea => currentVisual.activeArea!;
 
   @override
-  VidUsImage? get frameData => _subFrame ?? _frame;
+  VidUsImage? get frameData => _crossFrameContext?.currentFrame ?? _frame;
 
   @override
   List<IVisual> get visuals => _visuals ?? [];
@@ -204,11 +208,19 @@ class Application implements IApplication {
         // 添加活动测量项事件监听
         _activeMeasureItem!.featureUpdated
             .addListener(_onActiveMeasureItemFeatureUpdated);
-      }
-      if (_subFrame != null) {
-        // 清除附帧并恢复原始帧的Visual状态
-        _subFrame = null;
-        loadVisuals();
+
+        if (crossFrameContext != null) {
+          // TODO:!!!!!!!!!!!
+          // 清除附帧并恢复原始帧的Visual状态
+          crossFrameContext!.measureOver.emit(this, null);
+          crossFrameContext = null;
+          loadVisuals();
+        } else {
+          if ((value! is ITopMeasureItem) &&
+              (value as ITopMeasureItem).isCrossFrameMode) {
+            crossFrameTriggered.emit(this, null);
+          }
+        }
       }
 
       // 通知更新事件
@@ -244,6 +256,15 @@ class Application implements IApplication {
   @override
   IMeasureRecorder get recorder => _recorder;
 
+  @override
+  ICrossFrameContext? get crossFrameContext => _crossFrameContext;
+  @override
+  set crossFrameContext(ICrossFrameContext? val) {
+    if (val != _crossFrameContext) {
+      _crossFrameContext = val;
+    }
+  }
+
   @override
   late final FEventHandler<IMode> currentModeChanged;
 
@@ -270,6 +291,12 @@ class Application implements IApplication {
   @override
   late final FEventHandler<Size> displaySizeChanged;
 
+  @override
+  late final FEventHandler<void> crossFrameTriggered;
+
+  @override
+  late final FEventHandler<int> crossFrameAdded;
+
   @override
   void loadFrame(VidUsImage frame, [bool clearable = true]) {
     if (_frame == null) {
@@ -287,21 +314,13 @@ class Application implements IApplication {
       return;
     }
 
-    if (activeMeasureItem != null && activeMeasureItem! is ITopMeasureItem) {
-      if ((activeMeasureItem as ITopMeasureItem).isCrossFrameMode) {
-        // _frame = VidUsImage(
-        //   _frame!.index,
-        //   frame.width,
-        //   frame.height,
-        //   frame.imageData,
-        // );
-        // TODO: 暂不支持直接改index
-        _subFrame = frame;
-        // 仅更新frame数据
-        loadVisuals();
-        return;
-      }
+    if (crossFrameContext != null) {
+      // 仅更新frame数据
+      crossFrameContext!.switchFrame(frame);
+      loadVisuals();
+      return;
     }
+
     _frame = frame;
     if (clearable) {
       _resetFrameCache();

+ 116 - 0
lib/process/workspace/cross_frame.dart

@@ -0,0 +1,116 @@
+import 'package:fis_common/event/event_type.dart';
+import 'package:fis_measure/interfaces/process/items/item.dart';
+import 'package:fis_measure/interfaces/process/workspace/cross_frame.dart';
+import 'package:vid/us/vid_us_image.dart';
+
+class CrossFrameContext implements ICrossFrameContext {
+  late final String _originUrl;
+  late final VidUsImage _originFrame;
+  late final IMeasureItem _measureItem;
+  final Map<String, List<VidUsImage>> _imageFrameCache = {};
+
+  late VidUsImage? _currentFrame;
+  late String _currentUrl;
+
+  CrossFrameContext(String url, VidUsImage frame, IMeasureItem item) {
+    measureOver = FEventHandler<void>();
+    _currentUrl = _originUrl = url;
+    _currentFrame = _originFrame = frame;
+    _imageFrameCache[url] = [frame];
+    _measureItem = item;
+  }
+
+  @override
+  String get originUrl => _originUrl;
+
+  @override
+  String get currentUrl => _currentUrl;
+
+  @override
+  VidUsImage? get currentFrame => _currentFrame;
+
+  @override
+  int get originIndex => _originFrame.index;
+
+  @override
+  int? get currentIndex => _currentFrame?.index;
+
+  @override
+  IMeasureItem get measureItem => _measureItem;
+
+  @override
+  List<int> get recordIndexList =>
+      _imageFrameCache[_currentUrl]!.map((e) => e.index).toList();
+
+  @override
+  late final FEventHandler<void> measureOver;
+
+  @override
+  void switchImage(String url) {
+    if (url == _currentUrl) {
+      return;
+    }
+    _currentUrl = url;
+    _currentFrame = null;
+    if (!_imageFrameCache.containsKey(url)) {
+      if (_imageFrameCache.keys.length > 1) {
+        // 结束测量
+        measureOver.emit(this, null);
+        return;
+      }
+      _imageFrameCache[url] = [];
+    }
+  }
+
+  @override
+  void switchFrame(VidUsImage frame) {
+    if (frame == _currentFrame) {
+      return;
+    }
+    _currentFrame = frame;
+  }
+
+  @override
+  void recordFrame(int index) {
+    if (_currentFrame == null) {
+      return;
+    }
+    final records = _imageFrameCache[_currentUrl]!;
+    if (!records.contains(_currentFrame)) {
+      records.add(_currentFrame!);
+    }
+  }
+}
+
+class CrossFrameStore implements ICrossFrameStore {
+  CrossFrameStore() {
+    frameRestored = FEventHandler<int>();
+  }
+
+  @override
+  VidUsImage? get originFrame => throw UnimplementedError();
+
+  @override
+  int get originIndex => throw UnimplementedError();
+
+  @override
+  VidUsImage? get subFrame => throw UnimplementedError();
+
+  @override
+  int get subIndex => throw UnimplementedError();
+
+  @override
+  late final FEventHandler<int> frameRestored;
+
+  @override
+  Future<void> crossTo(VidUsImage? frame) async {
+    // TODO: implement crossTo
+    throw UnimplementedError();
+  }
+
+  @override
+  Future<void> restore() async {
+    // TODO: implement restore
+    throw UnimplementedError();
+  }
+}

+ 20 - 1
lib/process/workspace/measure_controller.dart

@@ -21,6 +21,7 @@ import 'package:fis_ui/index.dart';
 import 'package:get/get.dart';
 import 'package:vid/us/vid_us_image.dart';
 
+import 'cross_frame.dart';
 import 'measure_data_controller.dart';
 import 'package:universal_html/html.dart' as html;
 
@@ -32,6 +33,7 @@ class MeasureController implements IMeasureController {
   IPlayerController? _playerController;
   IApplication? _application;
   int _recursive = 0;
+  CrossFrameContext? _crossFrameContext;
 
   late bool _enableLoopPlayback;
   final ValueCallback<bool>? onLoopPlaybackEnableChanged;
@@ -193,6 +195,8 @@ class MeasureController implements IMeasureController {
   }
 
   Future<void> _buildApplication() async {
+    _application?.crossFrameTriggered.removeListener(_onCrossFrameTriggered);
+
     final dataChannel = playerController.dataChannel;
     final probe = dataChannel.probe;
     final is3rd = probe.name == Application.C_VID_THIRDPART_NAME;
@@ -204,7 +208,22 @@ class MeasureController implements IMeasureController {
     }
     Get.delete<IApplication>();
     _application = Get.put<IApplication>(app);
-    _application!.isSingleFrame = dataChannel.imageCount == 1;
+    app.isSingleFrame = dataChannel.imageCount == 1;
+    app.crossFrameTriggered.addListener(_onCrossFrameTriggered);
+    if (_crossFrameContext != null) {
+      // 传递跨帧上下文
+      app.crossFrameContext = _crossFrameContext;
+    }
+  }
+
+  void _onCrossFrameTriggered(_, e) {
+    final crossContext = CrossFrameContext(
+      currentVidUrl,
+      workingApplication.frameData!,
+      workingApplication.activeMeasureItem!,
+    );
+    _crossFrameContext = crossContext;
+    workingApplication.crossFrameContext = crossContext;
   }
 
   void _windowSetState(int index) {