arrow_annotation.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // ignore_for_file: constant_identifier_names
  2. import 'dart:math' as math;
  3. import 'package:fis_measure/interfaces/date_types/point.dart';
  4. import 'package:fis_measure/interfaces/enums/annotation.dart';
  5. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  6. import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:get/get.dart';
  9. import 'annotation.dart';
  10. /// 箭头注释
  11. class ArrowAnnotation extends AnnotationItem<ArrowAnnotationItemFeature> {
  12. ArrowAnnotation() : super(AnnotationType.arrow);
  13. @override
  14. bool onExecuteMouse(PointInfo args) {
  15. if (state == AnnotationStates.finish) {
  16. state = AnnotationStates.waiting;
  17. }
  18. if (state == AnnotationStates.waiting) {
  19. if (args.pointType == PointInfoType.mouseDown) {
  20. feature = ArrowAnnotationItemFeature(this, args);
  21. state = AnnotationStates.running;
  22. return true;
  23. } else {
  24. return false;
  25. }
  26. } else {
  27. if (args.pointType == PointInfoType.mouseUp) return false;
  28. feature?.endPoint = args;
  29. if (args.pointType == PointInfoType.mouseDown) {
  30. doFeatureFinish();
  31. }
  32. }
  33. return true;
  34. }
  35. PointInfo? startPoint;
  36. @override
  37. bool onExecuteTouch(PointInfo args) {
  38. if (state == AnnotationStates.finish) {
  39. if (args.pointType == PointInfoType.touchDown) {
  40. state = AnnotationStates.waiting;
  41. }
  42. }
  43. if (state == AnnotationStates.waiting) {
  44. switch (args.pointType) {
  45. case PointInfoType.touchDown:
  46. startPoint = args; // 设置线段起点
  47. break;
  48. case PointInfoType.touchUp:
  49. break; // 按下立即抬起无事发生
  50. case PointInfoType.touchMove:
  51. feature = ArrowAnnotationItemFeature(this, startPoint as DPoint);
  52. state = AnnotationStates.running; // 通过设置的起点开始一个绘制事件
  53. break;
  54. default:
  55. break;
  56. }
  57. } else if (state == AnnotationStates.running) {
  58. if (args.pointType == PointInfoType.touchUp) {
  59. doFeatureFinish();
  60. }
  61. if (args.pointType == PointInfoType.touchMove) {
  62. feature?.endPoint = args;
  63. }
  64. }
  65. // if (state == AnnotationStates.waiting) {
  66. // if (args.pointType == PointInfoType.mouseDown) {
  67. // feature = ArrowAnnotationItemFeature(this, args);
  68. // state = AnnotationStates.running;
  69. // return true;
  70. // } else {
  71. // return false;
  72. // }
  73. // } else {
  74. // if (args.pointType == PointInfoType.mouseUp) return false;
  75. // feature?.endPoint = args;
  76. // if (args.pointType == PointInfoType.mouseDown) {
  77. // doFeatureFinish();
  78. // }
  79. // }
  80. return true;
  81. }
  82. }
  83. class ArrowAnnotationItemFeature extends AnnotationItemFeature {
  84. static const C_ARRPW_ANGLE = 90.0; // 箭头角度
  85. static const C_ARROW_LENGTH = 8.0; // 中心线交叉点到终点的长度
  86. final _paint = Paint()
  87. ..color = Colors.yellow
  88. ..strokeWidth = 2
  89. ..style = PaintingStyle.stroke;
  90. ArrowAnnotationItemFeature(ArrowAnnotation ref, DPoint offset) : super(ref) {
  91. points.addAll([offset]);
  92. }
  93. DPoint get endPoint => points[1];
  94. set endPoint(DPoint value) {
  95. if (points.length > 1) {
  96. points[1] = value;
  97. } else {
  98. points.add(value);
  99. }
  100. }
  101. DPoint get startPoint => position;
  102. @override
  103. void paint(Canvas canvas, Size size) {
  104. double arrowLen =
  105. C_ARROW_LENGTH * Get.find<IApplication>().displayScaleRatio;
  106. final startOffset = startPoint.scale2Size(size).toOffset();
  107. final Offset endOffset;
  108. if (points.length < 2 || startPoint == endPoint) {
  109. endOffset = startOffset - const Offset(0, -1);
  110. } else {
  111. endOffset = points[1].scale2Size(size).toOffset();
  112. }
  113. canvas.drawLine(startOffset, endOffset, _paint);
  114. _drawArrow(canvas, startOffset, endOffset, arrowLen);
  115. }
  116. /// 绘制箭头
  117. void _drawArrow(Canvas canvas, Offset p1, Offset p2, double arrowLength) {
  118. final directionX = (p2.dx - p1.dx); // X轴向量方向
  119. final directionY = (p2.dy - p1.dy); // Y轴向量方向
  120. final center = _findArrowHeadCenter(p1, p2, arrowLength);
  121. const cuspAngle = C_ARRPW_ANGLE / 2; // 边线和中心线的角度
  122. const cuspRadians = cuspAngle * (math.pi / 180);
  123. // 边角到中心交叉点的距离
  124. final distanceCenter2Side = math.tan(cuspRadians) * arrowLength;
  125. final tan2AxisX = (p2.dy - p1.dy) / (p2.dx - p1.dx);
  126. // 中心线到X轴的弧度
  127. final radians2AxisX = math.atan(tan2AxisX).abs();
  128. // 在画布正坐标计算边点
  129. // 边点到X轴的距离
  130. final distance2AxisX = math.sin(radians2AxisX) * distanceCenter2Side;
  131. // 边点到Y轴的距离
  132. final distance2AxisY = math.cos(radians2AxisX) * distanceCenter2Side;
  133. double xRight = 0, yRight = 0;
  134. double xLeft = 0, yLeft = 0;
  135. if (directionX < 0) {
  136. if (directionY > 0) {
  137. // 右上->左下
  138. xRight = center.dx - distance2AxisX;
  139. yRight = center.dy - distance2AxisY;
  140. xLeft = center.dx + distance2AxisX;
  141. yLeft = center.dy + distance2AxisY;
  142. } else {
  143. // 右下->左上
  144. xRight = center.dx + distance2AxisX;
  145. yRight = center.dy - distance2AxisY;
  146. xLeft = center.dx - distance2AxisX;
  147. yLeft = center.dy + distance2AxisY;
  148. }
  149. } else {
  150. if (directionY > 0) {
  151. // 左上->右下
  152. xRight = center.dx - distance2AxisX;
  153. yRight = center.dy + distance2AxisY;
  154. xLeft = center.dx + distance2AxisX;
  155. yLeft = center.dy - distance2AxisY;
  156. } else {
  157. // 左下->右上
  158. xRight = center.dx + distance2AxisX;
  159. yRight = center.dy + distance2AxisY;
  160. xLeft = center.dx - distance2AxisX;
  161. yLeft = center.dy - distance2AxisY;
  162. }
  163. }
  164. final leftSidePoint = Offset(xLeft, yLeft);
  165. final rightSidePoint = Offset(xRight, yRight);
  166. canvas.drawLine(p2, leftSidePoint, _paint);
  167. canvas.drawLine(p2, rightSidePoint, _paint);
  168. }
  169. /// 找到箭头底部(两个边角的水平线)与直线的交叉点
  170. Offset _findArrowHeadCenter(Offset p1, Offset p2, double arrowLength) {
  171. final x1 = p1.dx, x2 = p2.dx;
  172. final y1 = p1.dy, y2 = p2.dy;
  173. // - 假设交叉点: p3 = (x3,y3)
  174. // ratio = distanc(p3-p2)/distance(p2-p1)
  175. // - 同时
  176. // ratio == (x3-x2)/(x1-x2)
  177. // ratio == (y3-y2)/(y1-y2)
  178. // - 可得
  179. // x3 = (x1-x2)*ratio + x2
  180. // y3 = (y1-y2)*ratio + y2
  181. final lineLength = (p2 - p1).distance;
  182. final ratio = arrowLength / lineLength;
  183. final dx = (ratio * (x1 - x2)) + x2;
  184. final dy = (ratio * (y1 - y2)) + y2;
  185. return Offset(dx, dy);
  186. }
  187. }