import 'dart:async'; import 'dart:math'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:get_storage/get_storage.dart'; import 'package:trackoffical_app/model.dart'; import '../../../model/game_map.dart'; import '../../../service/app.dart'; export 'package:get/get.dart'; export 'dart:typed_data'; export 'package:flutter/material.dart'; abstract class LayerController extends GetxController with GetSingleTickerProviderStateMixin{ GameMap? get gameMap; final mapTransformMatrix = Matrix4.identity().obs; Rx mapWidgetSize = Rx(null); final mapWidgetKey = GlobalKey(); Timer? _mapWidgetSizeTimer; double _mapScreenAndSrcScale = 1; final mapRotateCenter = Offset.zero.obs; var isEnableUserTouchTranslation = true; var isEnableUserTouchRotation = true; final cpRadiusMeter = 15.0.obs; // 比例尺 缩放后地图在屏幕的尺寸/米 实时 final _mapScale = 0.0.obs; // 比例尺 屏幕尺寸/米 原始 var mapScaleSrc = 0.0; var _isScaling = false; bool get isWidgetInit => mapWidgetSize.value != null; double get mapScale => _mapScale.value; var _matrixScale=1.0; var matrixRotation=0.0; final _translationUpdater = _ValueUpdater( onUpdate: (oldVal, newVal) => newVal - (oldVal ?? Offset.zero), ); final _rotationUpdater = _ValueUpdater( onUpdate: (oldVal, newVal) => newVal - (oldVal ?? 0), ); final _scaleUpdater = _ValueUpdater( onUpdate: (oldVal, newVal) => newVal / (oldVal ?? 1), ); var scaleMax = 9.0; var scaleMin = 1.0; void onMapSizeChange(Size size){ final map = gameMap; if(map!= null){ final fitted = applyBoxFit( BoxFit.contain, Size(map.width, map.height), Size(size.width, size.height)); _mapScreenAndSrcScale = fitted.destination.width / fitted.source.width; final screenLen = size.width; mapScaleSrc = screenLen / map.mapWidth.m; calculateMapScale(); } } Offset mapOffsetToScreen(Offset offset){ var thisOffset = Offset(offset.dx * _mapScreenAndSrcScale, offset.dy * _mapScreenAndSrcScale); final mr= mapTransformMatrix.value.applyToVector3Array([thisOffset.dx, thisOffset.dy, 0]); return Offset(mr[0], mr[1]); } Future positionToScreen(MPosition position)async{ final g = gameMap; if(g==null){ return Offset.zero; } final offset = await g.worldToPixel(position); return mapOffsetToScreen(offset); } void setRotate(double radians){ if(_isScaling){ return; } if(!isWidgetInit){ return; } if(mapRotateCenter.value==Offset.zero){ return; } final rotate = _getRotationMatrix(radians); mapTransformMatrix.value = rotate * mapTransformMatrix.value; // final focalPoint = mapRotateCenter.value; // if(focalPoint != Offset.zero){ // final rotate = _getRotationMatrix(radians); // if(rotate!= null){ // var matrix = mapTransformMatrix.value; // mapTransformMatrix.value = rotate * matrix; // } // } } void moveOnMapPointToScreen(Offset src, Offset dst){ final pOnScreen = mapOffsetToScreen(src); final dis = dst - pOnScreen; final tranM = Matrix4.translationValues(dis.dx, dis.dy, 0); mapTransformMatrix.value = tranM * mapTransformMatrix.value; } double meterToOnScreen(double meter){ return meter * _mapScale.value; } calculateMapScale(){ final gameMap = this.gameMap; if(gameMap==null){ return; } const p0Src = Offset(0, 0); final p1Src = Offset(gameMap.width, 0); final p0Dst = mapOffsetToScreen(p0Src); final p1Dst = mapOffsetToScreen(p1Src); final disPx = sqrt((p1Dst.dx - p0Dst.dx)* (p1Dst.dx - p0Dst.dx) + (p1Dst.dy - p0Dst.dy) * (p1Dst.dy - p0Dst.dy)); _mapScale.value = disPx / gameMap.mapWidth.m; } void onMapTouchScaleStart(ScaleStartDetails details) { _translationUpdater.value = details.focalPoint; _rotationUpdater.value = double.nan; _scaleUpdater.value = 1.0; _isScaling=true; } void onMapTouchScaleEnd(ScaleEndDetails details) { _isScaling=false; } void onMapTouchScaleUpdate(ScaleUpdateDetails details) { var matrix = mapTransformMatrix.value.clone(); // 平移 if(isEnableUserTouchTranslation){ final translationDelta = _translationUpdater.update(details.focalPoint); final translationDeltaMatrix = Matrix4.translationValues(translationDelta.dx, translationDelta.dy, 0); matrix = translationDeltaMatrix * matrix; } // 旋转 if(isEnableUserTouchRotation){ if (_rotationUpdater.value == null || _rotationUpdater.value!.isNaN) { _rotationUpdater.value = details.rotation; } else{ final rotationDelta = _rotationUpdater.update(details.rotation); final radians = rotationDelta+matrixRotation; final rotationDeltaMatrix = _getRotationMatrix(radians); matrix = rotationDeltaMatrix * matrix; } } // 缩放 final scaleDelta = _scaleUpdater.update(details.scale); matrix = _doScaleLimit(scaleDelta, matrix); mapTransformMatrix.value = matrix; calculateMapScale(); } void mapDoScale(double value){ mapTransformMatrix.value = _doScaleLimit(value, mapTransformMatrix.value); calculateMapScale(); } void resetMatrix(){ mapTransformMatrix.value = Matrix4.identity(); _rotationUpdater.value = 0; _translationUpdater.value = Offset.zero; _scaleUpdater.value = 1; _matrixScale=1; matrixRotation=0; calculateMapScale(); } Matrix4 _getRotationMatrix(double radians){ final delta = radians - matrixRotation; var c = cos(delta); var s = sin(delta); var dx = (1 - c) * mapRotateCenter.value.dx + s * mapRotateCenter.value.dy; var dy = (1 - c) * mapRotateCenter.value.dy - s * mapRotateCenter.value.dx; final rotationDeltaMatrix = Matrix4( c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); matrixRotation=radians; return rotationDeltaMatrix; } @override void onReady() { super.onReady(); _mapWidgetSizeTimer = Timer.periodic(20.milliseconds, (timer) { final renderBox = mapWidgetKey.currentContext?.findRenderObject() as RenderBox?; if(renderBox!= null){ final size = renderBox.size; if(size != Size.zero && size != mapWidgetSize.value){ mapWidgetSize.value = size; onMapSizeChange(size); } } }); } @override void onClose() { super.onClose(); _mapWidgetSizeTimer?.cancel(); } Matrix4 _doScaleLimit(double scaleDelta, Matrix4 matrix){ var scale = _matrixScale*scaleDelta; var delta = scaleDelta; if(scale < scaleMin){ delta = scaleMin / scale; } if( scale > scaleMax){ delta = scaleMax / scale; } return _doScale(delta, matrix); } Matrix4 _doScale(double scaleDelta, Matrix4 matrix){ // matrix = matrix.clone(); // final centerSrc = [mapRotateCenter.value.dx,mapRotateCenter.value.dy,0]; // final ma = matrix.clone(); // ma.invert(); // final center = ma.applyToVector3Array(centerSrc.toList()); // // matrix.translate(center[0], center[1]); // matrix.scale(scaleDelta, scaleDelta); // matrix.translate(-center[0], -center[1]); // _matrixScale*=scaleDelta; // return matrix; var dx = (1 - scaleDelta) * mapRotateCenter.value.dx; var dy = (1 - scaleDelta) * mapRotateCenter.value.dy; final scaleDeltaMatrix = Matrix4( scaleDelta, 0, 0, 0, 0, scaleDelta, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); _matrixScale*=scaleDelta; return scaleDeltaMatrix * matrix; } } typedef _OnUpdate = T Function(T? oldValue, T newValue); class _ValueUpdater { final _OnUpdate onUpdate; T? value; _ValueUpdater({required this.onUpdate}); T update(T newValue) { T updated = onUpdate(value, newValue); value = newValue; return updated; } } class StorageRx{ StorageRx(this._data): _state=_data.val.obs; final ReadWriteValue _data; final Rx _state; T get value => _state.value; set value(T v){ _state.value = v; _data.val = v; } }