import 'dart:math'; import 'dart:ui'; import 'package:vector_math/vector_math.dart'; import 'package:fis_measure/interfaces/date_types/point.dart'; import 'package:fis_measure/interfaces/enums/items.dart'; import 'package:fis_measure/interfaces/process/items/item_metas.dart'; import 'package:fis_measure/interfaces/process/items/item.dart'; import 'package:fis_measure/interfaces/process/items/types.dart'; import 'package:fis_measure/interfaces/process/workspace/point_info.dart'; import 'package:fis_measure/process/calcuators/calculator.dart'; import 'package:fis_measure/process/items/item.dart'; import 'package:fis_measure/process/items/item_feature.dart'; /// 独立的两个线段确定一个角度 class TwolineAngle extends MeasureItem<TwolineAngleFeature> { TwolineAngle(ItemMeta meta, IMeasureItem? parent) : super(meta, parent); static TwolineAngle createTwolineAngle(ItemMeta meta, [IMeasureItem? parent]) { TwolineAngle polygon = TwolineAngle(meta, parent); polygon.calculator = _AngleCalc(polygon); return polygon; } @override bool onExecuteMouse(PointInfo args) { if (state == ItemStates.finished) { if (args.pointType == PointInfoType.mouseDown) { state = ItemStates.waiting; } } if (state == ItemStates.waiting) { if (args.pointType == PointInfoType.mouseDown) { handleMouseDownWhileWaiting(args); } } else if (state == ItemStates.running) { if (feature == null) return false; if (args.pointType == PointInfoType.mouseUp) return false; final f = feature!; if (args.pointType == PointInfoType.mouseDown) { f.innerPoints.add(args); if (f.innerPoints.length == 5) { f.innerPoints.removeLast(); doFeatureFinish(); } } else { f.innerPoints.last = args; } doCalculate(); } return true; } DPoint lastStartPoint = DPoint(0, 0); // 上一个起点 bool isFirstPointNeedOffset = false; // 第一个点是否需要施加偏移 DPoint splineTouchStartPoint = DPoint(0, 0); // 轨迹触摸起始点 bool isOtherPointNeedOffset = false; // 其他点是否需要施加偏移 @override bool onExecuteTouch(PointInfo args) { if (state == ItemStates.finished) { if (args.pointType == PointInfoType.touchDown) { state = ItemStates.waiting; } } if (state == ItemStates.waiting) { if (isFirstPointNeedOffset) args.addOffset(0, -0.2); switch (args.pointType) { case PointInfoType.touchDown: handleTouchDownWhileWaiting(args); isFirstPointNeedOffset = false; break; case PointInfoType.touchUp: lastStartPoint = args; // 设置线段起点 state = ItemStates.running; feature?.innerPoints.first = args; break; // 按下立即抬起无事发生 case PointInfoType.touchMove: if (isMoveTargetOutOfRange(args)) return true; isFirstPointNeedOffset = true; feature?.innerPoints.first = args; break; default: break; } } else if (state == ItemStates.running) { final f = feature!; if (isOtherPointNeedOffset && f.innerPoints.length == 3) { args.addOffset(0, -0.2); } if (feature == null) return false; PointInfo newPoint = PointInfo.fromOffset( lastStartPoint .clone() .addVector(args - splineTouchStartPoint) .toOffset(), args.pointType); newPoint.hostVisualArea = args.hostVisualArea; if (args.pointType == PointInfoType.touchUp) { if (!isOtherPointNeedOffset) { f.innerPoints.last = args; lastStartPoint = args; doCalculate(); } else { lastStartPoint = newPoint; } if (f.innerPoints.length == 4) { doFeatureFinish(); } } if (args.pointType == PointInfoType.touchDown) { isOtherPointNeedOffset = false; splineTouchStartPoint = args; f.innerPoints.add(lastStartPoint); if (f.innerPoints.length == 3) { lastStartPoint = args; } } if (args.pointType == PointInfoType.touchMove) { if (isMoveTargetOutOfRange(newPoint)) return true; isOtherPointNeedOffset = true; f.innerPoints.last = newPoint; } doCalculate(); } return true; } void handleMouseDownWhileWaiting(PointInfo args) { // TODO: 判断是否当前area // 转换为Area逻辑位置 final point = args.toAreaLogicPoint(); feature = TwolineAngleFeature(this, point); if (args.hostVisualArea != null) { feature!.hostVisualArea = args.hostVisualArea; } state = ItemStates.running; } void handleTouchDownWhileWaiting(PointInfo args) { // TODO: 判断是否当前area // 转换为Area逻辑位置 final point = args.toAreaLogicPoint(); feature = TwolineAngleFeature.withOneStartPoint(this, point); if (args.hostVisualArea != null) { feature!.hostVisualArea = args.hostVisualArea; } } } class TwolineAngleFeature extends MeasureItemFeature { TwolineAngleFeature(IMeasureItem refItem, DPoint point) : super(refItem) { innerPoints.add(point.clone()); innerPoints.add(point.clone()); } TwolineAngleFeature.withOneStartPoint(IMeasureItem refItem, DPoint point) : super(refItem) { innerPoints.add(point.clone()); } @override void paint(Canvas canvas, Size size) { if (innerPoints.isEmpty) return; drawId(canvas, size); final innerOffsets = innerPoints.map((e) => convert2ViewPoint(size, e).toOffset()).toList(); final len = innerOffsets.length; if (len == 1) { drawVertex(canvas, innerOffsets[0]); } else if (len == 2) { final a = innerOffsets[0]; final b = innerOffsets[1]; canvas.drawLine(a, b, paintLinePan); } else if (len == 3) { final a = innerOffsets[0]; final b = innerOffsets[1]; canvas.drawLine(a, b, paintLinePan); drawVertex(canvas, innerOffsets[2]); } else if (len == 4) { final a = innerOffsets[0]; final b = innerOffsets[1]; final c = innerOffsets[2]; final d = innerOffsets[3]; canvas.drawLine(a, b, paintLinePan); canvas.drawLine(c, d, paintLinePan); //向量 A、B final vecA = Vector2(b.dx - a.dx, b.dy - a.dy); final vecB = Vector2(d.dx - c.dx, d.dy - c.dy); //计算向量夹角 final angle = vecA.angleToSigned(vecB); //计算射线cd的角度 final vecBAngle = vecB.angleToSigned(Vector2(1, 0)); // 计算射线ab与射线cd的交点 final Offset? p = _getIntersection(a, b, c, d); if (p != null) { // 绘制角度 canvas.drawArc(Rect.fromCircle(center: p, radius: 30), -vecBAngle, -angle, false, paintLinePan); //如果交点在线段外,线段要延长至交点 Offset? p1 = _getIntersection(a, b, p, Offset(0, p.dy)); if (p1 != null) { final vecA1 = Vector2(p1.dx - a.dx, p1.dy - a.dy).normalized() * 50; p1 += Offset(vecA1.x, vecA1.y); canvas.drawLine(a, p1, paintLinePan); } Offset? p2 = _getIntersection(c, d, p, Offset(0, p.dy)); if (p2 != null) { final vecB1 = Vector2(p2.dx - c.dx, p2.dy - c.dy).normalized() * 50; p2 += Offset(vecB1.x, vecB1.y); canvas.drawLine(c, p2, paintLinePan); } } } } /// 计算射线ab与射线cd的交点 Offset? _getIntersection(Offset a, Offset b, Offset c, Offset d) { final x1 = a.dx; final y1 = a.dy; final x2 = b.dx; final y2 = b.dy; final x3 = c.dx; final y3 = c.dy; final x4 = d.dx; final y4 = d.dy; final denominator = ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)); // 检查分母是否为零,即两条射线是否平行 if (denominator == 0) { return null; // 无交点 } final x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denominator; final y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denominator; return Offset(x, y); } } class _AngleCalc extends Calculator<TwolineAngle, double> { _AngleCalc(TwolineAngle ref) : super(ref); @override void calculate() { if (ref.feature == null) return; final feature = ref.feature!; final viewport = feature.hostVisualArea!.viewport!; final points = feature.innerPoints.map((e) => viewport.convert(e)).toList(); feature.values.clear(); double angle; if (points.length != 4) { angle = 0; } else { angle = calcAngle(points[0], points[1], points[2], points[3]); } for (var output in ref.meta.outputs) { if (output.name == MeasureTypes.Angle) { var value = roundDouble(angle, output.fractionalDigits); feature.updateFloatValue(output, value, output.unit); } } } static double calcAngle( DPoint pointsA, DPoint pointsB, DPoint pointsC, DPoint pointsD) { double finalAngleDegree = 0; //向量 A、B final vecA = Vector2(pointsB.x - pointsA.x, pointsB.y - pointsA.y); final vecB = Vector2(pointsD.x - pointsC.x, pointsD.y - pointsC.y); //计算向量夹角 final angle = vecA.angleToSigned(vecB); //转换为角度 finalAngleDegree = angle * 180 / pi; if (finalAngleDegree < 0) { finalAngleDegree = -finalAngleDegree; } return finalAngleDegree; } }