|
@@ -0,0 +1,308 @@
|
|
|
+
|
|
|
+
|
|
|
+import 'package:fis_common/event/event_type.dart';
|
|
|
+import 'package:fis_common/logger/logger.dart';
|
|
|
+import 'package:fis_measure/interfaces/base/version.dart';
|
|
|
+import 'package:fis_measure/interfaces/date_types/matrix.dart';
|
|
|
+import 'package:fis_measure/interfaces/date_types/point.dart';
|
|
|
+import 'package:fis_measure/interfaces/date_types/sr_segment_infos.dart';
|
|
|
+import 'package:fis_measure/interfaces/enums/uint.dart';
|
|
|
+import 'package:flutter/foundation.dart';
|
|
|
+
|
|
|
+import '../visuals/visual_area_interface.dart';
|
|
|
+import '../update_obj_base.dart';
|
|
|
+import '../visuals/normalization_coordinate.dart';
|
|
|
+import 'viewport_interface.dart';
|
|
|
+import 'image_boundary_interface.dart';
|
|
|
+import 'logical_coordinate_interface.dart';
|
|
|
+import 'physical_coordinate_interface.dart';
|
|
|
+import 'rect_region.dart';
|
|
|
+
|
|
|
+part 'viewport.ext.dart';
|
|
|
+
|
|
|
+abstract class ViewPort<TV extends IVisualArea, T extends IPhysicalCoordinate>
|
|
|
+ extends UpdateObjectBase implements IViewPort, ILogicalCoordinate {
|
|
|
+ static const int _RetryCountOnFailed = 5;
|
|
|
+
|
|
|
+ static final Version _CurrentVersion = Version(1, 0);
|
|
|
+
|
|
|
+ final physicalCoordinateChanged = FEventHandler<void>();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ int _retryOnDownloadFailed = 0;
|
|
|
+
|
|
|
+ @protected
|
|
|
+ final List<T> _availablePhysicals = <T>[];
|
|
|
+
|
|
|
+
|
|
|
+ DMatrix? _normalizationToLogicalConvert;
|
|
|
+
|
|
|
+
|
|
|
+ DMatrix? _logicalToNormalizationConvert;
|
|
|
+
|
|
|
+ bool _isLeftRight = false;
|
|
|
+ bool _isUpDown = false;
|
|
|
+ RectRegion? _region;
|
|
|
+ IImageBoundary? _imageBoundary;
|
|
|
+ T? _physical;
|
|
|
+ bool _physicalCoordinateReady = false;
|
|
|
+ bool _isDirty = false;
|
|
|
+
|
|
|
+ late final TV _visualArea;
|
|
|
+ late Unit? _xUnit;
|
|
|
+ late Unit? _yUnit;
|
|
|
+ late NormalizationCoordinate _normalization;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool get physicalCoordinateReady => _physicalCoordinateReady;
|
|
|
+ NormalizationCoordinate get normalization => _normalization;
|
|
|
+
|
|
|
+ @override
|
|
|
+ IImageBoundary? get imageBoundary => _imageBoundary;
|
|
|
+ @override
|
|
|
+ set imageBoundary(IImageBoundary? value) {
|
|
|
+ if (_imageBoundary != value) {
|
|
|
+ _imageBoundary = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ T? get physical => _physical;
|
|
|
+ set physical(T? value) {
|
|
|
+ if (_physical == value) return;
|
|
|
+
|
|
|
+ var updateItem = _physical as UpdateObjectBase?;
|
|
|
+ if (updateItem != null) {
|
|
|
+ updateItem.updated.removeListener(_onPhysicalUpdated);
|
|
|
+ }
|
|
|
+ _physical = value;
|
|
|
+ updateItem = _physical as UpdateObjectBase?;
|
|
|
+ if (updateItem != null) {
|
|
|
+
|
|
|
+
|
|
|
+ updateItem.updated.addListener(_onPhysicalUpdated);
|
|
|
+ }
|
|
|
+ logger.i(
|
|
|
+ "Working coordinate for view port on visual area:${_visualArea.visualAreaType.name} changed to ${_physical == null ? "null" : _physical.runtimeType}",
|
|
|
+ );
|
|
|
+ _onPhysicalCoordinateChanged();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool get isDirty => _isDirty;
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool get isFlipHorizontal => _isLeftRight;
|
|
|
+ @override
|
|
|
+ set isFlipHorizontal(bool value) {
|
|
|
+ if (_isLeftRight != value) {
|
|
|
+ _isLeftRight = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool get isFlipVertical => _isUpDown;
|
|
|
+ @override
|
|
|
+ set isFlipVertical(bool value) {
|
|
|
+ if (_isUpDown != value) {
|
|
|
+ _isUpDown = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ RectRegion? get region => _region;
|
|
|
+ @override
|
|
|
+ set region(RectRegion? value) {
|
|
|
+ if (_region != value) {
|
|
|
+ _region = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ TV get visualArea => _visualArea;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Unit? get xUnit => _xUnit;
|
|
|
+ @override
|
|
|
+ set xUnit(Unit? value) {
|
|
|
+ if (_xUnit != value) {
|
|
|
+ _xUnit = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Unit? get yUnit => _yUnit;
|
|
|
+ @override
|
|
|
+ set yUnit(Unit? value) {
|
|
|
+ if (_yUnit != value) {
|
|
|
+ _yUnit = value;
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ ViewPort(TV visualArea, Unit xUnit, Unit yUnit) {
|
|
|
+ _retryOnDownloadFailed = _RetryCountOnFailed;
|
|
|
+ _visualArea = visualArea;
|
|
|
+ _xUnit = xUnit;
|
|
|
+ _yUnit = yUnit;
|
|
|
+ _normalization = NormalizationCoordinate();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ void checkModify(int modificationType) {
|
|
|
+ bool isNeedSynchronize = true;
|
|
|
+ if (isNeedSynchronize) {
|
|
|
+ _isDirty = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool download() {
|
|
|
+ if (!isDirty) return true;
|
|
|
+
|
|
|
+ bool isValid = physical != null;
|
|
|
+ if (!isValid) return true;
|
|
|
+
|
|
|
+ IDisposable? locker;
|
|
|
+ try {
|
|
|
+ locker = lockOnUpdating();
|
|
|
+
|
|
|
+ updateNormalizationCoordinate();
|
|
|
+
|
|
|
+ bool successful = physical!.download(_visualArea, this);
|
|
|
+ _physicalCoordinateReady = successful;
|
|
|
+
|
|
|
+ _isDirty = false;
|
|
|
+ if (!successful) {
|
|
|
+ logger.w(
|
|
|
+ "ViewPort on visual area:${_visualArea.visualAreaType.name} download physicalcoordinate data from native failed!",
|
|
|
+ );
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } catch (ex) {
|
|
|
+ _physicalCoordinateReady = false;
|
|
|
+ _retryOnDownloadFailed--;
|
|
|
+ if (_retryOnDownloadFailed == 0) {
|
|
|
+ _retryOnDownloadFailed = _RetryCountOnFailed;
|
|
|
+ _isDirty = false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ if (locker != null) {
|
|
|
+ locker.dispose();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void updateNormalizationCoordinate() {
|
|
|
+ bool isModified = normalization.download(_visualArea);
|
|
|
+ if (isModified) {
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DPoint convert(DPoint pointInNormalization) {
|
|
|
+ return _normalizationToLogicalConvert!
|
|
|
+ .transformWidthPoint(pointInNormalization);
|
|
|
+ }
|
|
|
+
|
|
|
+ VecPoint convertBackVec(VecPoint pointInLogical) {
|
|
|
+ return _logicalToNormalizationConvert!
|
|
|
+ .transformWithVecPoint(pointInLogical);
|
|
|
+ }
|
|
|
+
|
|
|
+ DPoint convertBack(DPoint pointInLogical) {
|
|
|
+ return _logicalToNormalizationConvert!.transformWidthPoint(pointInLogical);
|
|
|
+ }
|
|
|
+
|
|
|
+ DMatrix getConvertMatrix() {
|
|
|
+ return _normalizationToLogicalConvert!;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool checkBoundary() {
|
|
|
+ if (physical != null && imageBoundary != null) {
|
|
|
+ return physical!.checkBoundary(imageBoundary!);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void registerPhysicalCoordinate(T? physicalCoordinate, bool isRegister) {
|
|
|
+ if (physicalCoordinate == null) {
|
|
|
+ logger
|
|
|
+ .e("Can't register/unregister a null instance physical coordinate!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (_availablePhysicals.contains(physicalCoordinate)) {
|
|
|
+ _availablePhysicals.remove(physicalCoordinate);
|
|
|
+ }
|
|
|
+ if (isRegister) {
|
|
|
+ _availablePhysicals.add(physicalCoordinate);
|
|
|
+ logger.i(
|
|
|
+ "Reg coordinate ${physicalCoordinate.runtimeType}. visual area:${_visualArea.visualAreaType.name}",
|
|
|
+ );
|
|
|
+ physical = physicalCoordinate;
|
|
|
+ } else {
|
|
|
+ logger.i(
|
|
|
+ "UnReg coordinate ${physicalCoordinate.runtimeType}. visual area:${_visualArea.visualAreaType.name}",
|
|
|
+ );
|
|
|
+ if (physical == physicalCoordinate) {
|
|
|
+ physical =
|
|
|
+ _availablePhysicals.isNotEmpty ? _availablePhysicals.last : null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @protected
|
|
|
+ void resetPhysicalCoordinates() {
|
|
|
+ _availablePhysicals.clear();
|
|
|
+ physical = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onUpdated() {
|
|
|
+ _logicalToNormalizationConvert = buildConvertMatrix();
|
|
|
+ _normalizationToLogicalConvert = _logicalToNormalizationConvert;
|
|
|
+ if (_normalizationToLogicalConvert!.hasInverse) {
|
|
|
+ _normalizationToLogicalConvert!.invert();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @protected
|
|
|
+ DMatrix buildConvertMatrix() {
|
|
|
+ var logicalToNormalization = DMatrix();
|
|
|
+ double horizontalOffset =
|
|
|
+ -1 * (isFlipHorizontal ? region!.right : region!.left);
|
|
|
+ double verticalOffset =
|
|
|
+ -1 * (isFlipVertical ? region!.bottom : region!.top);
|
|
|
+ double xScale =
|
|
|
+ (isFlipHorizontal ? -1 : 1) / (region!.right - region!.left);
|
|
|
+ double yScale = (isFlipVertical ? -1 : 1) / (region!.bottom - region!.top);
|
|
|
+ logicalToNormalization.translate(horizontalOffset, verticalOffset);
|
|
|
+ logicalToNormalization.scale(xScale, yScale);
|
|
|
+ return logicalToNormalization;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ void _onPhysicalUpdated(Object sender, dynamic eventArgs) {
|
|
|
+ tryUpdate();
|
|
|
+ }
|
|
|
+
|
|
|
+ void _onPhysicalCoordinateChanged() {
|
|
|
+ physicalCoordinateChanged.emit(this, null);
|
|
|
+ }
|
|
|
+}
|