import 'dart:ui'; import 'dart:math' as math; import 'package:fis_measure/interfaces/date_types/point.dart'; import 'package:fis_measure/interfaces/date_types/rect_region.dart'; import 'package:fis_measure/interfaces/enums/items.dart'; import 'package:fis_measure/interfaces/process/items/item.dart'; import 'package:fis_measure/interfaces/process/items/item_metas.dart'; import 'package:fis_measure/interfaces/process/workspace/point_info.dart'; import 'package:fis_measure/process/calcuators/ray.dart'; import 'package:fis_measure/process/items/item.dart'; import 'package:fis_measure/process/items/item_feature.dart'; import 'package:fis_measure/utils/canvas.dart'; class Ray extends MeasureItem { double _initializeAngle = 0; double _initializeAngleVal = 0; bool _switchInitialAngle = false; Ray(ItemMeta meta, IMeasureItem? parent) : super(meta, parent); double get initializeAngle => _initializeAngle; set initializeAngle(double value) { if (value != _initializeAngle) { _initializeAngleVal = value; _updateInitializeAngle(); } } bool get switchInitialAngle => _switchInitialAngle; set switchInitialAngle(bool value) { if (value != _switchInitialAngle) { _switchInitialAngle = value; _updateInitializeAngle(); } } @override bool onExecuteMouse(PointInfo args) { if (state == ItemStates.finished || state == ItemStates.waiting) { if (args.pointType == PointInfoType.mouseMove) { feature = RayFeature(this, args); feature!.hostVisualArea = args.hostVisualArea; final viewport = args.hostVisualArea!.viewport!; feature!.isReverse = viewport.isFlipHorizontal ^ viewport.isFlipVertical; state = ItemStates.running; } } if (state == ItemStates.running) { if (args.pointType == PointInfoType.mouseUp) return false; feature?.point = args; doCalculate(); if (args.pointType == PointInfoType.mouseDown) { doFeatureFinish(); } } return true; } // 是否需要结束当前的测量过程,用于移动端的交互 bool isNeedFinish = true; bool isFirstTouch = true; DPoint touchStartPoint = DPoint(0, 0); DPoint lastLinePosition = DPoint(0, 0); @override bool onExecuteTouch(PointInfo args) { if (state == ItemStates.waiting || state == ItemStates.finished) { if (args.pointType == PointInfoType.touchMove) { if (measuredFeatures.isEmpty) { feature = RayFeature(this, args); feature!.hostVisualArea = args.hostVisualArea; final viewport = args.hostVisualArea!.viewport!; feature!.isReverse = viewport.isFlipHorizontal ^ viewport.isFlipVertical; } else { feature = measuredFeatures.first; } state = ItemStates.running; } } if (state == ItemStates.running) { if (args.pointType == PointInfoType.touchDown) { touchStartPoint = args; lastLinePosition = feature?.point ?? args; isNeedFinish = true; isFirstTouch = feature?.point == null; } if (args.pointType == PointInfoType.touchMove) { isNeedFinish = false; if (isFirstTouch) { if (isMoveTargetOutOfRange(args)) return true; feature?.point = args; } else { PointInfo newPoint = PointInfo.fromOffset( lastLinePosition .clone() .addVector(args - touchStartPoint) .toOffset(), args.pointType); newPoint.hostVisualArea = args.hostVisualArea; if (isMoveTargetOutOfRange(newPoint)) return true; feature?.point = newPoint; } doCalculate(); } if (args.pointType == PointInfoType.touchUp && isNeedFinish) { doFeatureFinish(); } } return true; } static Ray createRay(ItemMeta meta, [IMeasureItem? parent]) { final ray = Ray(meta, parent); ray.calculator = RayDepthCal(ray); return ray; } void _updateInitializeAngle() { int switchNumber = switchInitialAngle ? -1 : 1; _initializeAngle = _initializeAngleVal * switchNumber; } } class RayFeature extends MeasureItemFeature { double _angle = 0; bool _isReverse = false; RayFeature(Ray refItem, DPoint point) : super(refItem) { innerPoints.add(point.clone()); angle = refItem.initializeAngle; } DPoint get point => innerPoints[0]; set point(DPoint value) { innerPoints[0] = value; } double get angle => _angle; set angle(double value) { if (value != _angle) { _angle = value; } } bool get isReverse => _isReverse; set isReverse(bool value) { if (value != _isReverse) { _isReverse = value; } } RectRegion get viewRegion => hostVisualArea!.viewport!.area.layoutRegion!; @override void paint(Canvas canvas, Size size) { if (innerPoints.isEmpty) return; final curItemName = refItem.displayName; final offset = convert2ViewPoint(size, point).toOffset(); final rect = Rect.fromLTWH( viewRegion.left * size.width, viewRegion.top * size.height, viewRegion.width * size.width, viewRegion.height * size.height); List twoPoint = [const Offset(0, 0), const Offset(0, 0)]; final idText = '$id.${refItem.displayName}'; switch (curItemName) { case "Baseline": twoPoint = calcLinePoint(rect, offset, 0); drawCustomId(canvas, size, twoPoint[1], idText); break; case "Line α": twoPoint = calcLinePoint(rect, offset, angle); drawCustomId(canvas, size, twoPoint[1], idText); break; case "Line β": twoPoint = calcLinePoint(rect, offset, 180 - angle); drawCustomId(canvas, size, twoPoint[0], idText); break; default: break; } canvas.drawDashLine(twoPoint[0], twoPoint[1], 1, 10, paintLinePan); } List calcLinePoint(Rect rect, Offset innerPoint, double angle) { List twoPoint = [const Offset(0, 0), const Offset(0, 0)]; if (!rect.contains(innerPoint)) return twoPoint; final tan = math.tan(angle * math.pi / 180); final x = innerPoint.dx; final y = innerPoint.dy; final d1 = x - y / tan; twoPoint[0] = Offset(d1, 0); final d2 = (rect.height - y) / tan + x; twoPoint[1] = Offset(d2, rect.height); if (angle <= 90) { if (d1 < rect.left) { twoPoint[0] = Offset(rect.left, y - tan * (x - rect.left)); } if (d2 > rect.right) { twoPoint[1] = Offset(rect.right, y + tan * (rect.right - x)); } } else { if (d1 > rect.right) { twoPoint[0] = Offset(rect.right, y - tan * (x - rect.right)); } if (d2 < rect.left) { twoPoint[1] = Offset(rect.left, y + tan * (rect.left - x)); } } return twoPoint; } }