map_status.dart 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'dart:typed_data';
  4. import 'package:flutter/material.dart';
  5. import 'package:get/get.dart';
  6. import 'package:trackoffical_app/logger.dart';
  7. import 'package:trackoffical_app/model/game_map.dart';
  8. import 'package:trackoffical_app/service/app.dart';
  9. import 'package:trackoffical_app/service/game/game_model.dart';
  10. import 'package:vibration/vibration.dart';
  11. import '../../model/map_mode.dart';
  12. import '../../widget/matrix_gesture_detector.dart';
  13. import 'package:vector_math/vector_math_64.dart' as vec;
  14. class MapStatus {
  15. var canVibrate = false;
  16. var mapImageData = Uint8List(0).obs;
  17. var gameMapData = GameMap();
  18. final GameModel _model = Get.find();
  19. Rx<Matrix4> get matrix => _model.mapTransformMatrix;
  20. Rx<MapMode> get mapMode => _model.mapMode;
  21. double get compassAngle => _model.compassRadiansFused.value;
  22. var lastCompassRadians = 0.0;
  23. var vibrateAccumulation = 0.0;
  24. var isTouching = false;
  25. double picFirstScale = 1;
  26. var isSetBeginMatrix = false;
  27. final isShowMapScale = false.obs;
  28. Timer? _showMapScaleTimer;
  29. double _mapLenMeter=1;
  30. // 比例尺 屏幕尺寸/米 实时
  31. final mapScale = 0.0.obs;
  32. // 比例尺 屏幕尺寸/米 原始
  33. var mapScaleSrc = 0.0;
  34. double get rotateRadian {
  35. final ogRm = matrix.value.clone();
  36. double radian = MatrixGestureDetector.decomposeToValues(ogRm).rotation;
  37. return radian;
  38. }
  39. double get compassPlantRadian =>_model.compassPlantRadian;
  40. final _translationUpdater = _ValueUpdater<Offset>(
  41. onUpdate: (oldVal, newVal) => newVal - (oldVal ?? Offset.zero),
  42. );
  43. final _rotationUpdater = _ValueUpdater<double>(
  44. onUpdate: (oldVal, newVal) => newVal - (oldVal ?? 0),
  45. );
  46. final _scaleUpdater = _ValueUpdater<double>(
  47. onUpdate: (oldVal, newVal) => newVal / (oldVal ?? 1),
  48. );
  49. Future<void> resetMatrix() async{
  50. matrix.value = Matrix4.identity();
  51. lastCompassRadians = 0.0;
  52. _rotationUpdater.value = 0;
  53. _translationUpdater.value = Offset.zero;
  54. _scaleUpdater.value = 1;
  55. isSetBeginMatrix=false;
  56. matrix.value = _doScale(4, matrix.value);
  57. const p0Src = Offset(0, 0);
  58. final p1Src = Offset(gameMapData.width, 0);
  59. final mapTopLeftLocation = await gameMapData.pixelToWorld(p0Src);
  60. final mapTopRightLocation = await gameMapData.pixelToWorld(p1Src);
  61. final disKm = mapTopLeftLocation.distance(mapTopRightLocation);
  62. _mapLenMeter = disKm.km;
  63. final screenLen = App.to.screenSize.width;
  64. mapScaleSrc = screenLen / _mapLenMeter;
  65. calculateMapScale();
  66. }
  67. Offset picOffsetToScreen(Offset offset){
  68. var thisOffset = Offset(offset.dx * picFirstScale, offset.dy * picFirstScale);
  69. final mr= matrix.value.applyToVector3Array([thisOffset.dx, thisOffset.dy, 0]);
  70. return Offset(mr[0], mr[1]);
  71. }
  72. void movePicPointTo(Offset src, Offset dst){
  73. final pOnScreen = picOffsetToScreen(src);
  74. final dis = dst - pOnScreen;
  75. final tranM = Matrix4.translationValues(dis.dx, dis.dy, 0);
  76. matrix.value = tranM * matrix.value;
  77. }
  78. Matrix4? _getRotationMatrix(
  79. double radiansDelta,
  80. Offset focalPoint,
  81. {bool willVibrate=false}) {
  82. if (_rotationUpdater.value == null || _rotationUpdater.value!.isNaN) {
  83. _rotationUpdater.value = radiansDelta;
  84. } else {
  85. final rotationDelta = _rotationUpdater.update(radiansDelta);
  86. var c = cos(rotationDelta);
  87. var s = sin(rotationDelta);
  88. var dx = (1 - c) * focalPoint.dx + s * focalPoint.dy;
  89. var dy = (1 - c) * focalPoint.dy - s * focalPoint.dx;
  90. final rotationDeltaMatrix = Matrix4(
  91. c, s, 0, 0,
  92. -s, c, 0, 0,
  93. 0, 0, 1, 0,
  94. dx, dy, 0, 1);
  95. if(willVibrate){
  96. vibrateAccumulation += vec.degrees(rotationDelta);
  97. if (vibrateAccumulation.abs() >= 5) {
  98. vibrateAccumulation = 0;
  99. if (canVibrate) {
  100. Vibration.vibrate(duration: 20, repeat: 1);
  101. }
  102. }
  103. }
  104. return rotationDeltaMatrix;
  105. }
  106. return null;
  107. }
  108. void setRotate(double radians) {
  109. final focalPoint = _model.mapRotateCenter;
  110. if(focalPoint != null){
  111. final rotate = _getRotationMatrix(radians, focalPoint);
  112. if(rotate!= null){
  113. var matrix = this.matrix.value;
  114. this.matrix.value = rotate * matrix;
  115. }
  116. }
  117. }
  118. void mapModeSwitch() {
  119. resetMatrix();
  120. switch (mapMode.value) {
  121. case MapMode.original:
  122. mapMode.value = MapMode.compass;
  123. break;
  124. case MapMode.compass:
  125. mapMode.value = MapMode.original;
  126. break;
  127. }
  128. }
  129. void onMapTouchScaleStart(ScaleStartDetails details) {
  130. isTouching=true;
  131. _translationUpdater.value = details.focalPoint;
  132. _rotationUpdater.value = double.nan;
  133. _scaleUpdater.value = 1.0;
  134. }
  135. Matrix4 _doScale(double scaleDelta, Matrix4 matrix){
  136. final focalPoint = _model.mapRotateCenter!;
  137. var dx = (1 - scaleDelta) * focalPoint.dx;
  138. var dy = (1 - scaleDelta) * focalPoint.dy;
  139. final scaleDeltaMatrix = Matrix4(
  140. scaleDelta, 0, 0, 0,
  141. 0, scaleDelta, 0, 0,
  142. 0, 0, 1, 0,
  143. dx, dy, 0, 1);
  144. return scaleDeltaMatrix * matrix;
  145. }
  146. void onMapTouchScaleUpdate(ScaleUpdateDetails details, Size screen) {
  147. final focalPoint = _model.mapRotateCenter!;
  148. var matrix = this.matrix.value;
  149. // 平移
  150. if(!_model.isLockScreenCenterToMyPositionSystem){
  151. final translationDelta = _translationUpdater.update(details.focalPoint);
  152. final translationDeltaMatrix =
  153. Matrix4.translationValues(translationDelta.dx, translationDelta.dy, 0);
  154. matrix = translationDeltaMatrix * matrix;
  155. }
  156. // 缩放
  157. final scale = matrix[0].abs();
  158. if (details.scale != 1.0) {
  159. final zoomOutLimit = scale > 0.5 || details.scale > 1;
  160. // final zoomInLimit = scale < 8 || details.scale < 1;
  161. final zoomInLimit = mapScale < 1.7 || details.scale < 1;
  162. if(zoomOutLimit && zoomInLimit){
  163. final scaleDelta = _scaleUpdater.update(details.scale);
  164. matrix = _doScale(scaleDelta, matrix);
  165. }
  166. }
  167. // 旋转
  168. var rotationDelta = 0.0;
  169. var willVibrate = false;
  170. switch (mapMode.value) {
  171. case MapMode.original:
  172. rotationDelta = details.rotation;
  173. willVibrate = true;
  174. break;
  175. case MapMode.compass:
  176. break;
  177. }
  178. if(rotationDelta != 0){
  179. final rotationDeltaMatrix = _getRotationMatrix(
  180. rotationDelta, focalPoint, willVibrate: willVibrate);
  181. if(rotationDeltaMatrix!= null){
  182. matrix = rotationDeltaMatrix * matrix;
  183. }
  184. }
  185. this.matrix.value = matrix;
  186. calculateMapScale();
  187. }
  188. calculateMapScale(){
  189. const p0Src = Offset(0, 0);
  190. final p1Src = Offset(gameMapData.width, 0);
  191. final p0Dst = picOffsetToScreen(p0Src);
  192. final p1Dst = picOffsetToScreen(p1Src);
  193. final disPx = sqrt((p1Dst.dx - p0Dst.dx)* (p1Dst.dx - p0Dst.dx)
  194. + (p1Dst.dy - p0Dst.dy) * (p1Dst.dy - p0Dst.dy));
  195. final scale = disPx / _mapLenMeter;
  196. mapScale.value = scale;
  197. isShowMapScale.value = true;
  198. _showMapScaleTimer?.cancel();
  199. _showMapScaleTimer = Timer(3.seconds, () {
  200. isShowMapScale.value = false;
  201. });
  202. }
  203. }
  204. typedef _OnUpdate<T> = T Function(T? oldValue, T newValue);
  205. class _ValueUpdater<T> {
  206. final _OnUpdate<T> onUpdate;
  207. T? value;
  208. _ValueUpdater({required this.onUpdate});
  209. T update(T newValue) {
  210. T updated = onUpdate(value, newValue);
  211. value = newValue;
  212. return updated;
  213. }
  214. }