straightline.dart 12 KB

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