straightline.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import 'dart:ui';
  2. import 'dart:math' as math;
  3. import 'package:fis_measure/interfaces/date_types/point.dart';
  4. import 'package:fis_measure/interfaces/enums/items.dart';
  5. import 'package:fis_measure/interfaces/process/items/item.dart';
  6. import 'package:fis_measure/interfaces/process/items/item_metas.dart';
  7. import 'package:fis_measure/interfaces/process/items/types.dart';
  8. import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
  9. import 'package:fis_measure/process/calcuators/time_motion.dart';
  10. import 'package:fis_measure/process/items/item.dart';
  11. import 'package:fis_measure/utils/canvas.dart';
  12. import 'package:fis_measure/view/gesture/cross_position_indicator.dart';
  13. import 'package:fis_measure/view/gesture/positioned_cursor.dart';
  14. import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart';
  15. import 'package:fis_ui/index.dart';
  16. import 'package:flutter/foundation.dart';
  17. import 'package:get/get.dart';
  18. import 'package:vid/us/vid_us_mode.dart';
  19. import '../calcuators/distance.dart';
  20. import '../items/item_feature.dart';
  21. /// 直线
  22. class StraightLine extends MeasureItem<StraightLineFeature> {
  23. late final touchState = Get.find<ITouchPointState>();
  24. late final mouseState = Get.find<IMouseState>();
  25. StraightLine(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
  26. static StraightLine createDistance(ItemMeta meta, [IMeasureItem? parent]) {
  27. StraightLine sraightLine = StraightLine(meta, parent);
  28. sraightLine.calculator = DistanceCal(sraightLine);
  29. return sraightLine;
  30. }
  31. static StraightLine createSlope(ItemMeta meta, [IMeasureItem? parent]) {
  32. StraightLine sraightLine = StraightLine(meta, parent);
  33. sraightLine.calculator = SlopeCal(sraightLine);
  34. return sraightLine;
  35. }
  36. static StraightLine createSlopeDoppler(ItemMeta meta,
  37. [IMeasureItem? parent]) {
  38. StraightLine sraightLine = StraightLine(meta, parent);
  39. sraightLine.calculator = SlopeDopplerCal(sraightLine);
  40. return sraightLine;
  41. }
  42. static StraightLine createVerticalDistance(ItemMeta meta,
  43. [IMeasureItem? parent]) {
  44. StraightLine sraightLine = StraightLine(meta, parent);
  45. sraightLine.calculator = VerticalDistanceCal(sraightLine);
  46. return sraightLine;
  47. }
  48. static StraightLine createTimeSpan(ItemMeta meta, [IMeasureItem? parent]) {
  49. StraightLine sraightLine = StraightLine(meta, parent);
  50. sraightLine.calculator = TimeSpanCal(sraightLine);
  51. return sraightLine;
  52. }
  53. static StraightLine createLvDpDt(ItemMeta meta, [IMeasureItem? parent]) {
  54. StraightLine sraightLine = StraightLine(meta, parent);
  55. sraightLine.repeatableEditable = true;
  56. sraightLine.calculator = DpDtCal(sraightLine);
  57. return sraightLine;
  58. }
  59. static StraightLine createVolume(ItemMeta meta, [IMeasureItem? parent]) {
  60. StraightLine sraightLine = StraightLine(meta, parent);
  61. sraightLine.calculator = VolumeOneDistanceCal(sraightLine);
  62. return sraightLine;
  63. }
  64. @override
  65. bool onExecuteMouse(PointInfo args) {
  66. if (state == ItemStates.finished) {
  67. if (args.pointType == PointInfoType.mouseDown) {
  68. state = ItemStates.waiting;
  69. } else {
  70. return false;
  71. }
  72. }
  73. if (state == ItemStates.waiting) {
  74. if (args.pointType == PointInfoType.mouseDown) {
  75. handleMouseDownWhileWaiting(args);
  76. }
  77. } else if (state == ItemStates.running) {
  78. if (args.pointType == PointInfoType.mouseUp) return false;
  79. feature?.endPoint = args;
  80. doCalculate();
  81. if (args.pointType == PointInfoType.mouseDown) {
  82. doFeatureFinish();
  83. ///重置十字样式
  84. changeCrossIndicatorStyle(CrossIndicatorStyle.nomal);
  85. }
  86. }
  87. return true;
  88. }
  89. PointInfo? firstTouchStartPoint;
  90. bool isFirstPointMove = false;
  91. DPoint? secondTouchStartPoint;
  92. bool isSecondPointMove = false;
  93. @override
  94. bool onExecuteTouch(PointInfo args) {
  95. if (state == ItemStates.finished) {
  96. if (args.pointType == PointInfoType.touchDown) {
  97. isFirstPointMove = false;
  98. isSecondPointMove = false;
  99. state = ItemStates.waiting;
  100. }
  101. }
  102. if (state == ItemStates.waiting) {
  103. switch (args.pointType) {
  104. case PointInfoType.touchDown:
  105. firstTouchStartPoint = args; // 设置线段起点
  106. handleTouchBeforeStart(firstTouchStartPoint!); // 开始绘制前动态设置第一个起点
  107. break;
  108. case PointInfoType.touchMove:
  109. isFirstPointMove = true;
  110. args.addOffset(0, -0.2); // 添加偏移
  111. if (isMoveTargetOutOfRange(args)) return true;
  112. feature?.startPoint = args;
  113. feature?.endPoint = args;
  114. break;
  115. case PointInfoType.touchUp:
  116. if (isFirstPointMove) {
  117. args.addOffset(0, -0.2); // 添加偏移
  118. }
  119. firstTouchStartPoint = args; // 设置线段起点
  120. feature?.startPoint = args;
  121. state = ItemStates.running;
  122. break;
  123. default:
  124. break;
  125. }
  126. } else if (state == ItemStates.running) {
  127. switch (args.pointType) {
  128. case PointInfoType.touchDown:
  129. secondTouchStartPoint = args;
  130. break;
  131. case PointInfoType.touchMove:
  132. PointInfo newPoint = PointInfo.fromOffset(
  133. firstTouchStartPoint!
  134. .clone()
  135. .addVector(args - secondTouchStartPoint!)
  136. .toOffset(),
  137. args.pointType);
  138. newPoint.hostVisualArea = args.hostVisualArea;
  139. if (isMoveTargetOutOfRange(newPoint)) return true;
  140. isSecondPointMove = true;
  141. doCalculate();
  142. feature?.endPoint = newPoint;
  143. break;
  144. case PointInfoType.touchUp:
  145. if (!isSecondPointMove) {
  146. feature?.endPoint = args;
  147. doCalculate();
  148. }
  149. doFeatureFinish();
  150. break;
  151. default:
  152. break;
  153. }
  154. }
  155. return true;
  156. }
  157. void handleMouseDownWhileWaiting(PointInfo args) {
  158. // TODO: 判断是否当前area
  159. // 转换为Area逻辑位置
  160. final point = args.toAreaLogicPoint();
  161. // feature = StraightLineFeature(this, point, point);
  162. if (args.hostVisualArea != null) {
  163. handleTissueTM(args.hostVisualArea!.mode.modeType, point);
  164. feature!.hostVisualArea = args.hostVisualArea;
  165. }
  166. state = ItemStates.running;
  167. }
  168. void handleTouchBeforeStart(PointInfo args) {
  169. // 转换为Area逻辑位置
  170. final point = args.toAreaLogicPoint();
  171. // feature = StraightLineFeature(this, point, point);
  172. if (args.hostVisualArea != null) {
  173. handleTissueTM(args.hostVisualArea!.mode.modeType, point);
  174. feature!.hostVisualArea = args.hostVisualArea;
  175. }
  176. }
  177. /// 处理TissueTimeMotion模式
  178. void handleTissueTM(VidUsModeType mode, DPoint point) {
  179. if (mode == VidUsModeType.TissueTM || mode == VidUsModeType.Doppler) {
  180. switch (meta.measureType) {
  181. case MeasureTypes.TimeSpan:
  182. feature = StraightLineTimeMotionFeature(this, point, point,
  183. ifHorizontal: false);
  184. changeCrossIndicatorStyle(CrossIndicatorStyle.vertical);
  185. break;
  186. case MeasureTypes.VerticalDistance:
  187. feature = StraightLineTimeMotionFeature(this, point, point);
  188. changeCrossIndicatorStyle(CrossIndicatorStyle.horizontal);
  189. break;
  190. case MeasureTypes.Slope:
  191. feature = StraightLineSlopeFeature(this, point, point);
  192. break;
  193. case MeasureTypes.SlopeDoppler:
  194. feature = StraightLineSlopeFeature(this, point, point);
  195. break;
  196. case MeasureTypes.Pht:
  197. feature = StraightLinePhtFeature(this, point, point);
  198. changeCrossIndicatorStyle(CrossIndicatorStyle.vertical);
  199. break;
  200. case MeasureTypes.LvDpDt:
  201. feature = StraightLineSlopeFeature(this, point, point);
  202. break;
  203. default:
  204. }
  205. } else {
  206. if (meta.name.toLowerCase().contains("radius")) {
  207. feature = SemicircleFuture(this, point, point);
  208. } else {
  209. feature = StraightLineFeature(this, point, point);
  210. }
  211. }
  212. }
  213. void changeCrossIndicatorStyle(CrossIndicatorStyle e) {
  214. if (kIsMobile) {
  215. touchState.crossIndicatorStyleChanged.emit(this, e);
  216. } else {
  217. mouseState.crossIndicatorStyleChanged.emit(this, e);
  218. }
  219. }
  220. }
  221. class StraightLineFeature extends MeasureItemFeature {
  222. StraightLineFeature(
  223. IMeasureItem refItem,
  224. DPoint startPoint,
  225. DPoint endPoint,
  226. ) : super(refItem) {
  227. innerPoints.add(startPoint);
  228. innerPoints.add(endPoint);
  229. }
  230. /// 起点
  231. DPoint get startPoint => innerPoints[0];
  232. set startPoint(DPoint value) => innerPoints[0] = value;
  233. /// 终点
  234. DPoint get endPoint => innerPoints[1];
  235. set endPoint(DPoint value) => innerPoints[1] = value;
  236. /// 长度
  237. double get length => (endPoint - startPoint).length;
  238. @override
  239. void paint(Canvas canvas, Size size) {
  240. if (startPoint == endPoint && kIsWeb) return;
  241. drawId(canvas, size, idText);
  242. final startOffset = convert2ViewPoint(size, startPoint).toOffset();
  243. final endOffset = convert2ViewPoint(size, endPoint).toOffset();
  244. final distanceOffset = endOffset - startOffset;
  245. if (distanceOffset.distance < measureData.getMinCursorDistance()) {
  246. drawMiniVertex(canvas, startOffset);
  247. drawMiniVertex(canvas, endOffset, isActive);
  248. } else {
  249. drawVertex(canvas, startOffset);
  250. drawVertex(canvas, endOffset, isActive);
  251. }
  252. canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan);
  253. }
  254. }
  255. class StraightLineTimeMotionFeature extends StraightLineFeature {
  256. StraightLineTimeMotionFeature(
  257. IMeasureItem refItem, DPoint startPoint, DPoint endPoint,
  258. {this.ifHorizontal = true})
  259. : super(refItem, startPoint, endPoint);
  260. final bool ifHorizontal;
  261. @override
  262. void paint(Canvas canvas, Size size) {
  263. if (startPoint == endPoint && kIsWeb) return;
  264. drawId(canvas, size, idText);
  265. final startOffset = convert2ViewPoint(size, startPoint).toOffset();
  266. final endOffset = convert2ViewPoint(size, endPoint).toOffset();
  267. if (ifHorizontal) {
  268. drawMark(canvas, startOffset);
  269. drawMark(canvas, Offset(startOffset.dx, endOffset.dy), isActive);
  270. } else {
  271. drawMark(canvas, startOffset, false, ifHorizontal);
  272. drawMark(
  273. canvas, Offset(endOffset.dx, startOffset.dy), isActive, ifHorizontal);
  274. }
  275. }
  276. }
  277. class StraightLineSlopeFeature extends StraightLineFeature {
  278. StraightLineSlopeFeature(
  279. IMeasureItem refItem, DPoint startPoint, DPoint endPoint)
  280. : super(refItem, startPoint, endPoint);
  281. @override
  282. void paint(Canvas canvas, Size size) {
  283. if (startPoint == endPoint && kIsWeb) return;
  284. drawId(canvas, size, idText);
  285. final startOffset = convert2ViewPoint(size, startPoint).toOffset();
  286. drawVertex(canvas, startOffset);
  287. final endOffset = convert2ViewPoint(size, endPoint).toOffset();
  288. canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan);
  289. drawVertex(canvas, endOffset, isActive);
  290. }
  291. }
  292. class StraightLinePhtFeature extends StraightLineFeature {
  293. StraightLinePhtFeature(
  294. IMeasureItem refItem, DPoint startPoint, DPoint endPoint)
  295. : super(refItem, startPoint, endPoint);
  296. DPoint get regionCenter => hostVisualArea!.displayRegion.center;
  297. @override
  298. void paint(Canvas canvas, Size size) {
  299. if (startPoint == endPoint && kIsWeb) return;
  300. drawId(canvas, size, idText);
  301. final distanceToCenter = (startPoint.y - regionCenter.y);
  302. //TODO:[Gavin] 偏移量比例系数待精确
  303. final endPointY = startPoint.y - distanceToCenter / 4;
  304. final startOffset = convert2ViewPoint(size, startPoint).toOffset();
  305. final endOffset =
  306. convert2ViewPoint(size, DPoint(endPoint.x, endPointY)).toOffset();
  307. drawVertex(canvas, startOffset, false);
  308. canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan);
  309. drawVertex(canvas, endOffset, isActive);
  310. }
  311. }
  312. /// 半圆
  313. class SemicircleFuture extends StraightLineFeature {
  314. SemicircleFuture(super.refItem, super.startPoint, super.endPoint);
  315. @override
  316. void paint(Canvas canvas, Size size) {
  317. if (startPoint == endPoint && kIsWeb) return;
  318. drawId(canvas, size, idText);
  319. final center = convert2ViewPoint(size, startPoint).toOffset();
  320. final activeOffset = convert2ViewPoint(size, endPoint).toOffset();
  321. final distanceOffset = activeOffset - center;
  322. // 计算半径
  323. final radius = distanceOffset.distance;
  324. // 活动点到圆心,在X轴正方向的角度
  325. final activeAngle = math.atan2(distanceOffset.dy, distanceOffset.dx);
  326. // 半圆其实点,activeAngle向前90°角
  327. final startAngle = activeAngle - math.pi * 0.5;
  328. // 半圆
  329. const sweepAngle = math.pi;
  330. // 绘制半圆
  331. canvas.drawArc(
  332. Rect.fromCircle(center: center, radius: radius),
  333. startAngle,
  334. sweepAngle,
  335. false,
  336. paintLinePan,
  337. );
  338. }
  339. }