| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- 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<Size?> 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<Offset>(
- onUpdate: (oldVal, newVal) => newVal - (oldVal ?? Offset.zero),
- );
- final _rotationUpdater = _ValueUpdater<double>(
- onUpdate: (oldVal, newVal) => newVal - (oldVal ?? 0),
- );
- final _scaleUpdater = _ValueUpdater<double>(
- 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<Offset> 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 = <double>[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> = T Function(T? oldValue, T newValue);
- class _ValueUpdater<T> {
- final _OnUpdate<T> onUpdate;
- T? value;
- _ValueUpdater({required this.onUpdate});
- T update(T newValue) {
- T updated = onUpdate(value, newValue);
- value = newValue;
- return updated;
- }
- }
- class StorageRx<T>{
- StorageRx(this._data): _state=_data.val.obs;
- final ReadWriteValue<T> _data;
- final Rx<T> _state;
- T get value => _state.value;
- set value(T v){
- _state.value = v;
- _data.val = v;
- }
- }
|