layer_controller.dart 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'package:get/get.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:get_storage/get_storage.dart';
  6. import 'package:trackoffical_app/model.dart';
  7. import '../../../model/game_map.dart';
  8. import '../../../service/app.dart';
  9. export 'package:get/get.dart';
  10. export 'dart:typed_data';
  11. export 'package:flutter/material.dart';
  12. abstract class LayerController extends GetxController
  13. with GetSingleTickerProviderStateMixin{
  14. GameMap? get gameMap;
  15. final mapTransformMatrix = Matrix4.identity().obs;
  16. Rx<Size?> mapWidgetSize = Rx(null);
  17. final mapWidgetKey = GlobalKey();
  18. Timer? _mapWidgetSizeTimer;
  19. double _mapScreenAndSrcScale = 1;
  20. final mapRotateCenter = Offset.zero.obs;
  21. var isEnableUserTouchTranslation = true;
  22. var isEnableUserTouchRotation = true;
  23. final cpRadiusMeter = 15.0.obs;
  24. // 比例尺 缩放后地图在屏幕的尺寸/米 实时
  25. final _mapScale = 0.0.obs;
  26. // 比例尺 屏幕尺寸/米 原始
  27. var mapScaleSrc = 0.0;
  28. var _isScaling = false;
  29. bool get isWidgetInit => mapWidgetSize.value != null;
  30. double get mapScale => _mapScale.value;
  31. var _matrixScale=1.0;
  32. var matrixRotation=0.0;
  33. final _translationUpdater = _ValueUpdater<Offset>(
  34. onUpdate: (oldVal, newVal) => newVal - (oldVal ?? Offset.zero),
  35. );
  36. final _rotationUpdater = _ValueUpdater<double>(
  37. onUpdate: (oldVal, newVal) => newVal - (oldVal ?? 0),
  38. );
  39. final _scaleUpdater = _ValueUpdater<double>(
  40. onUpdate: (oldVal, newVal) => newVal / (oldVal ?? 1),
  41. );
  42. var scaleMax = 9.0;
  43. var scaleMin = 1.0;
  44. void onMapSizeChange(Size size){
  45. final map = gameMap;
  46. if(map!= null){
  47. final fitted = applyBoxFit(
  48. BoxFit.contain,
  49. Size(map.width, map.height),
  50. Size(size.width, size.height));
  51. _mapScreenAndSrcScale = fitted.destination.width / fitted.source.width;
  52. final screenLen = size.width;
  53. mapScaleSrc = screenLen / map.mapWidth.m;
  54. calculateMapScale();
  55. }
  56. }
  57. Offset mapOffsetToScreen(Offset offset){
  58. var thisOffset = Offset(offset.dx * _mapScreenAndSrcScale, offset.dy * _mapScreenAndSrcScale);
  59. final mr= mapTransformMatrix.value.applyToVector3Array([thisOffset.dx, thisOffset.dy, 0]);
  60. return Offset(mr[0], mr[1]);
  61. }
  62. Future<Offset> positionToScreen(MPosition position)async{
  63. final g = gameMap;
  64. if(g==null){
  65. return Offset.zero;
  66. }
  67. final offset = await g.worldToPixel(position);
  68. return mapOffsetToScreen(offset);
  69. }
  70. void setRotate(double radians){
  71. if(_isScaling){
  72. return;
  73. }
  74. if(!isWidgetInit){
  75. return;
  76. }
  77. if(mapRotateCenter.value==Offset.zero){
  78. return;
  79. }
  80. final rotate = _getRotationMatrix(radians);
  81. mapTransformMatrix.value = rotate * mapTransformMatrix.value;
  82. // final focalPoint = mapRotateCenter.value;
  83. // if(focalPoint != Offset.zero){
  84. // final rotate = _getRotationMatrix(radians);
  85. // if(rotate!= null){
  86. // var matrix = mapTransformMatrix.value;
  87. // mapTransformMatrix.value = rotate * matrix;
  88. // }
  89. // }
  90. }
  91. void moveOnMapPointToScreen(Offset src, Offset dst){
  92. final pOnScreen = mapOffsetToScreen(src);
  93. final dis = dst - pOnScreen;
  94. final tranM = Matrix4.translationValues(dis.dx, dis.dy, 0);
  95. mapTransformMatrix.value = tranM * mapTransformMatrix.value;
  96. }
  97. double meterToOnScreen(double meter){
  98. return meter * _mapScale.value;
  99. }
  100. calculateMapScale(){
  101. final gameMap = this.gameMap;
  102. if(gameMap==null){
  103. return;
  104. }
  105. const p0Src = Offset(0, 0);
  106. final p1Src = Offset(gameMap.width, 0);
  107. final p0Dst = mapOffsetToScreen(p0Src);
  108. final p1Dst = mapOffsetToScreen(p1Src);
  109. final disPx = sqrt((p1Dst.dx - p0Dst.dx)* (p1Dst.dx - p0Dst.dx)
  110. + (p1Dst.dy - p0Dst.dy) * (p1Dst.dy - p0Dst.dy));
  111. _mapScale.value = disPx / gameMap.mapWidth.m;
  112. }
  113. void onMapTouchScaleStart(ScaleStartDetails details) {
  114. _translationUpdater.value = details.focalPoint;
  115. _rotationUpdater.value = double.nan;
  116. _scaleUpdater.value = 1.0;
  117. _isScaling=true;
  118. }
  119. void onMapTouchScaleEnd(ScaleEndDetails details) {
  120. _isScaling=false;
  121. }
  122. void onMapTouchScaleUpdate(ScaleUpdateDetails details) {
  123. var matrix = mapTransformMatrix.value.clone();
  124. // 平移
  125. if(isEnableUserTouchTranslation){
  126. final translationDelta = _translationUpdater.update(details.focalPoint);
  127. final translationDeltaMatrix =
  128. Matrix4.translationValues(translationDelta.dx, translationDelta.dy, 0);
  129. matrix = translationDeltaMatrix * matrix;
  130. }
  131. // 旋转
  132. if(isEnableUserTouchRotation){
  133. if (_rotationUpdater.value == null || _rotationUpdater.value!.isNaN) {
  134. _rotationUpdater.value = details.rotation;
  135. } else{
  136. final rotationDelta = _rotationUpdater.update(details.rotation);
  137. final radians = rotationDelta+matrixRotation;
  138. final rotationDeltaMatrix = _getRotationMatrix(radians);
  139. matrix = rotationDeltaMatrix * matrix;
  140. }
  141. }
  142. // 缩放
  143. final scaleDelta = _scaleUpdater.update(details.scale);
  144. matrix = _doScaleLimit(scaleDelta, matrix);
  145. mapTransformMatrix.value = matrix;
  146. calculateMapScale();
  147. }
  148. void mapDoScale(double value){
  149. mapTransformMatrix.value = _doScaleLimit(value, mapTransformMatrix.value);
  150. calculateMapScale();
  151. }
  152. void resetMatrix(){
  153. mapTransformMatrix.value = Matrix4.identity();
  154. _rotationUpdater.value = 0;
  155. _translationUpdater.value = Offset.zero;
  156. _scaleUpdater.value = 1;
  157. _matrixScale=1;
  158. matrixRotation=0;
  159. calculateMapScale();
  160. }
  161. Matrix4 _getRotationMatrix(double radians){
  162. final delta = radians - matrixRotation;
  163. var c = cos(delta);
  164. var s = sin(delta);
  165. var dx = (1 - c) * mapRotateCenter.value.dx + s * mapRotateCenter.value.dy;
  166. var dy = (1 - c) * mapRotateCenter.value.dy - s * mapRotateCenter.value.dx;
  167. final rotationDeltaMatrix = Matrix4(
  168. c, s, 0, 0,
  169. -s, c, 0, 0,
  170. 0, 0, 1, 0,
  171. dx, dy, 0, 1);
  172. matrixRotation=radians;
  173. return rotationDeltaMatrix;
  174. }
  175. @override
  176. void onReady() {
  177. super.onReady();
  178. _mapWidgetSizeTimer = Timer.periodic(20.milliseconds, (timer) {
  179. final renderBox = mapWidgetKey.currentContext?.findRenderObject() as RenderBox?;
  180. if(renderBox!= null){
  181. final size = renderBox.size;
  182. if(size != Size.zero && size != mapWidgetSize.value){
  183. mapWidgetSize.value = size;
  184. onMapSizeChange(size);
  185. }
  186. }
  187. });
  188. }
  189. @override
  190. void onClose() {
  191. super.onClose();
  192. _mapWidgetSizeTimer?.cancel();
  193. }
  194. Matrix4 _doScaleLimit(double scaleDelta, Matrix4 matrix){
  195. var scale = _matrixScale*scaleDelta;
  196. var delta = scaleDelta;
  197. if(scale < scaleMin){
  198. delta = scaleMin / scale;
  199. }
  200. if( scale > scaleMax){
  201. delta = scaleMax / scale;
  202. }
  203. return _doScale(delta, matrix);
  204. }
  205. Matrix4 _doScale(double scaleDelta, Matrix4 matrix){
  206. // matrix = matrix.clone();
  207. // final centerSrc = <double>[mapRotateCenter.value.dx,mapRotateCenter.value.dy,0];
  208. // final ma = matrix.clone();
  209. // ma.invert();
  210. // final center = ma.applyToVector3Array(centerSrc.toList());
  211. //
  212. // matrix.translate(center[0], center[1]);
  213. // matrix.scale(scaleDelta, scaleDelta);
  214. // matrix.translate(-center[0], -center[1]);
  215. // _matrixScale*=scaleDelta;
  216. // return matrix;
  217. var dx = (1 - scaleDelta) * mapRotateCenter.value.dx;
  218. var dy = (1 - scaleDelta) * mapRotateCenter.value.dy;
  219. final scaleDeltaMatrix = Matrix4(
  220. scaleDelta, 0, 0, 0,
  221. 0, scaleDelta, 0, 0,
  222. 0, 0, 1, 0,
  223. dx, dy, 0, 1);
  224. _matrixScale*=scaleDelta;
  225. return scaleDeltaMatrix * matrix;
  226. }
  227. }
  228. typedef _OnUpdate<T> = T Function(T? oldValue, T newValue);
  229. class _ValueUpdater<T> {
  230. final _OnUpdate<T> onUpdate;
  231. T? value;
  232. _ValueUpdater({required this.onUpdate});
  233. T update(T newValue) {
  234. T updated = onUpdate(value, newValue);
  235. value = newValue;
  236. return updated;
  237. }
  238. }
  239. class StorageRx<T>{
  240. StorageRx(this._data): _state=_data.val.obs;
  241. final ReadWriteValue<T> _data;
  242. final Rx<T> _state;
  243. T get value => _state.value;
  244. set value(T v){
  245. _state.value = v;
  246. _data.val = v;
  247. }
  248. }