// 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/workspace/application.dart'; import 'package:fis_measure/interfaces/process/workspace/point_info.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'annotation.dart'; /// 箭头注释 class ArrowAnnotation extends AnnotationItem<ArrowAnnotationItemFeature> { ArrowAnnotation() : super(AnnotationType.arrow); @override bool onExecuteMouse(PointInfo args) { if (state == AnnotationStates.finish) { state = AnnotationStates.waiting; } if (state == AnnotationStates.waiting) { if (args.pointType == PointInfoType.mouseDown) { feature = ArrowAnnotationItemFeature(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; } PointInfo? startPoint; @override bool onExecuteTouch(PointInfo args) { if (state == AnnotationStates.finish) { if (args.pointType == PointInfoType.touchDown) { state = AnnotationStates.waiting; } } if (state == AnnotationStates.waiting) { switch (args.pointType) { case PointInfoType.touchDown: startPoint = args; // 设置线段起点 break; case PointInfoType.touchUp: break; // 按下立即抬起无事发生 case PointInfoType.touchMove: feature = ArrowAnnotationItemFeature(this, startPoint as DPoint); state = AnnotationStates.running; // 通过设置的起点开始一个绘制事件 break; default: break; } } else if (state == AnnotationStates.running) { if (args.pointType == PointInfoType.touchUp) { doFeatureFinish(); } if (args.pointType == PointInfoType.touchMove) { feature?.endPoint = args; } } // if (state == AnnotationStates.waiting) { // if (args.pointType == PointInfoType.mouseDown) { // feature = ArrowAnnotationItemFeature(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; } } class ArrowAnnotationItemFeature extends AnnotationItemFeature { static const C_ARRPW_ANGLE = 90.0; // 箭头角度 static const C_ARROW_LENGTH = 8.0; // 中心线交叉点到终点的长度 final _paint = Paint() ..color = Colors.yellow ..strokeWidth = 2 ..style = PaintingStyle.stroke; ArrowAnnotationItemFeature(ArrowAnnotation ref, DPoint offset) : super(ref) { points.addAll([offset]); } DPoint get endPoint => points[1]; set endPoint(DPoint value) { if (points.length > 1) { points[1] = value; } else { points.add(value); } } DPoint get startPoint => position; @override void paint(Canvas canvas, Size size) { double arrowLen = C_ARROW_LENGTH * Get.find<IApplication>().displayScaleRatio; final startOffset = startPoint.scale2Size(size).toOffset(); final Offset endOffset; if (points.length < 2 || startPoint == endPoint) { endOffset = startOffset - const Offset(0, -1); } else { endOffset = points[1].scale2Size(size).toOffset(); } canvas.drawLine(startOffset, endOffset, _paint); _drawArrow(canvas, startOffset, endOffset, arrowLen); } /// 绘制箭头 void _drawArrow(Canvas canvas, Offset p1, Offset p2, double arrowLength) { final directionX = (p2.dx - p1.dx); // X轴向量方向 final directionY = (p2.dy - p1.dy); // Y轴向量方向 final center = _findArrowHeadCenter(p1, p2, arrowLength); const cuspAngle = C_ARRPW_ANGLE / 2; // 边线和中心线的角度 const cuspRadians = cuspAngle * (math.pi / 180); // 边角到中心交叉点的距离 final distanceCenter2Side = math.tan(cuspRadians) * arrowLength; 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, double arrowLength) { 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 = arrowLength / lineLength; final dx = (ratio * (x1 - x2)) + x2; final dy = (ratio * (y1 - y2)) + y2; return Offset(dx, dy); } }