|
@@ -0,0 +1,153 @@
|
|
|
+// ignore_for_file: constant_identifier_names
|
|
|
+
|
|
|
+import 'dart:math' as math;
|
|
|
+import 'package:fis_measure/interfaces/date_types/point.dart';
|
|
|
+import 'package:fis_measure/interfaces/enums/annotation.dart';
|
|
|
+import 'package:fis_measure/interfaces/process/annotations/annotation.dart';
|
|
|
+import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+
|
|
|
+import 'annotation.dart';
|
|
|
+
|
|
|
+/// 箭头注释
|
|
|
+class ArrowAnnotation extends AnnotationItem {
|
|
|
+ ArrowAnnotation() : super(AnnotationType.arrow);
|
|
|
+
|
|
|
+ @override
|
|
|
+ ArrowAnnotationFeature? get feature =>
|
|
|
+ super.feature as ArrowAnnotationFeature?;
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool onExecuteMouse(PointInfo args) {
|
|
|
+ if (state == AnnotationStates.finish) {
|
|
|
+ state = AnnotationStates.waiting;
|
|
|
+ }
|
|
|
+ if (state == AnnotationStates.waiting) {
|
|
|
+ if (args.pointType == PointInfoType.mouseDown) {
|
|
|
+ feature = ArrowAnnotationFeature(this, args);
|
|
|
+ state = AnnotationStates.running;
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (args.pointType == PointInfoType.mouseUp) return false;
|
|
|
+
|
|
|
+ feature?.endPoint = args;
|
|
|
+ if (args.pointType == PointInfoType.mouseDown) {
|
|
|
+ doFeatureFinish();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool onExecuteTouch(PointInfo args) {
|
|
|
+ // TODO: implement onExecuteTouch
|
|
|
+ throw UnimplementedError();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class ArrowAnnotationFeature extends AnnotationFeature {
|
|
|
+ static const C_ARRPW_ANGLE = 90.0; // 箭头角度
|
|
|
+ static const C_ARROW_LENGTH = 20; // 中心线交叉点到终点的长度
|
|
|
+ final _paint = Paint()
|
|
|
+ ..color = Colors.red
|
|
|
+ ..strokeWidth = 2
|
|
|
+ ..style = PaintingStyle.stroke;
|
|
|
+
|
|
|
+ ArrowAnnotationFeature(ArrowAnnotation ref, DPoint offset) : super(ref) {
|
|
|
+ points.addAll([offset, offset]);
|
|
|
+ }
|
|
|
+
|
|
|
+ DPoint get endPoint => points[1];
|
|
|
+ set endPoint(DPoint value) => points[1] = value;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void draw(Canvas canvas, Size size, Offset offset) {
|
|
|
+ final startPoint = points[0].toOffset();
|
|
|
+ final endPoint = points[1].toOffset();
|
|
|
+ canvas.drawLine(startPoint, endPoint, _paint);
|
|
|
+
|
|
|
+ _drawArrow(canvas, startPoint, endPoint);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 绘制箭头
|
|
|
+ void _drawArrow(Canvas canvas, Offset p1, Offset p2) {
|
|
|
+ final directionX = (p2.dx - p1.dx); // X轴向量方向
|
|
|
+ final directionY = (p2.dy - p1.dy); // Y轴向量方向
|
|
|
+ final center = _findArrowHeadCenter(p1, p2);
|
|
|
+
|
|
|
+ const cuspAngle = C_ARRPW_ANGLE / 2; // 边线和中心线的角度
|
|
|
+ const cuspRadians = cuspAngle * (math.pi / 180);
|
|
|
+ // 边角到中心交叉点的距离
|
|
|
+ final distanceCenter2Side = math.tan(cuspRadians) * C_ARROW_LENGTH;
|
|
|
+
|
|
|
+ final tan2AxisX = (p2.dy - p1.dy) / (p2.dx - p1.dx);
|
|
|
+ // 中心线到X轴的弧度
|
|
|
+ final radians2AxisX = math.atan(tan2AxisX).abs();
|
|
|
+
|
|
|
+ // 在画布正坐标计算边点
|
|
|
+ // 边点到X轴的距离
|
|
|
+ final distance2AxisX = math.sin(radians2AxisX) * distanceCenter2Side;
|
|
|
+ // 边点到Y轴的距离
|
|
|
+ final distance2AxisY = math.cos(radians2AxisX) * distanceCenter2Side;
|
|
|
+
|
|
|
+ double xRight = 0, yRight = 0;
|
|
|
+ double xLeft = 0, yLeft = 0;
|
|
|
+ if (directionX < 0) {
|
|
|
+ if (directionY > 0) {
|
|
|
+ // 右上->左下
|
|
|
+ xRight = center.dx - distance2AxisX;
|
|
|
+ yRight = center.dy - distance2AxisY;
|
|
|
+ xLeft = center.dx + distance2AxisX;
|
|
|
+ yLeft = center.dy + distance2AxisY;
|
|
|
+ } else {
|
|
|
+ // 右下->左上
|
|
|
+ xRight = center.dx + distance2AxisX;
|
|
|
+ yRight = center.dy - distance2AxisY;
|
|
|
+ xLeft = center.dx - distance2AxisX;
|
|
|
+ yLeft = center.dy + distance2AxisY;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (directionY > 0) {
|
|
|
+ // 左上->右下
|
|
|
+ xRight = center.dx - distance2AxisX;
|
|
|
+ yRight = center.dy + distance2AxisY;
|
|
|
+ xLeft = center.dx + distance2AxisX;
|
|
|
+ yLeft = center.dy - distance2AxisY;
|
|
|
+ } else {
|
|
|
+ // 左下->右上
|
|
|
+ xRight = center.dx + distance2AxisX;
|
|
|
+ yRight = center.dy + distance2AxisY;
|
|
|
+ xLeft = center.dx - distance2AxisX;
|
|
|
+ yLeft = center.dy - distance2AxisY;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ final leftSidePoint = Offset(xLeft, yLeft);
|
|
|
+ final rightSidePoint = Offset(xRight, yRight);
|
|
|
+ canvas.drawLine(p2, leftSidePoint, _paint);
|
|
|
+ canvas.drawLine(p2, rightSidePoint, _paint);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 找到箭头底部(两个边角的水平线)与直线的交叉点
|
|
|
+ Offset _findArrowHeadCenter(Offset p1, Offset p2) {
|
|
|
+ final x1 = p1.dx, x2 = p2.dx;
|
|
|
+ final y1 = p1.dy, y2 = p2.dy;
|
|
|
+ // - 假设交叉点: p3 = (x3,y3)
|
|
|
+ // ratio = distanc(p3-p2)/distance(p2-p1)
|
|
|
+ // - 同时
|
|
|
+ // ratio == (x3-x2)/(x1-x2)
|
|
|
+ // ratio == (y3-y2)/(y1-y2)
|
|
|
+ // - 可得
|
|
|
+ // x3 = (x1-x2)*ratio + x2
|
|
|
+ // y3 = (y1-y2)*ratio + y2
|
|
|
+ final lineLength = (p2 - p1).distance;
|
|
|
+ final ratio = C_ARROW_LENGTH / lineLength;
|
|
|
+ final dx = (ratio * (x1 - x2)) + x2;
|
|
|
+ final dy = (ratio * (y1 - y2)) + y2;
|
|
|
+ return Offset(dx, dy);
|
|
|
+ }
|
|
|
+}
|