import 'dart:ui'; import 'dart:math' as math; 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.dart'; import 'package:fis_measure/interfaces/process/items/item_metas.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/time_motion.dart'; import 'package:fis_measure/process/items/item.dart'; import 'package:fis_measure/utils/canvas.dart'; import 'package:fis_measure/view/gesture/cross_position_indicator.dart'; import 'package:fis_measure/view/gesture/positioned_cursor.dart'; import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart'; import 'package:fis_ui/index.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:vid/us/vid_us_mode.dart'; import '../calcuators/distance.dart'; import '../items/item_feature.dart'; /// 直线 class StraightLine extends MeasureItem { late final touchState = Get.find(); late final mouseState = Get.find(); StraightLine(ItemMeta meta, IMeasureItem? parent) : super(meta, parent); static StraightLine createDistance(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = DistanceCal(sraightLine); return sraightLine; } static StraightLine createSlope(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = SlopeCal(sraightLine); return sraightLine; } static StraightLine createSlopeDoppler(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = SlopeDopplerCal(sraightLine); return sraightLine; } static StraightLine createVerticalDistance(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = VerticalDistanceCal(sraightLine); return sraightLine; } static StraightLine createTimeSpan(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = TimeSpanCal(sraightLine); return sraightLine; } static StraightLine createLvDpDt(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.repeatableEditable = true; sraightLine.calculator = DpDtCal(sraightLine); return sraightLine; } static StraightLine createVolume(ItemMeta meta, [IMeasureItem? parent]) { StraightLine sraightLine = StraightLine(meta, parent); sraightLine.calculator = VolumeOneDistanceCal(sraightLine); return sraightLine; } @override bool onExecuteMouse(PointInfo args) { if (state == ItemStates.finished) { if (args.pointType == PointInfoType.mouseDown) { state = ItemStates.waiting; } else { return false; } } if (state == ItemStates.waiting) { if (args.pointType == PointInfoType.mouseDown) { handleMouseDownWhileWaiting(args); } } else if (state == ItemStates.running) { if (args.pointType == PointInfoType.mouseUp) return false; feature?.endPoint = args; doCalculate(); if (args.pointType == PointInfoType.mouseDown) { doFeatureFinish(); ///重置十字样式 changeCrossIndicatorStyle(CrossIndicatorStyle.nomal); } } return true; } PointInfo? firstTouchStartPoint; bool isFirstPointMove = false; DPoint? secondTouchStartPoint; bool isSecondPointMove = false; @override bool onExecuteTouch(PointInfo args) { if (state == ItemStates.finished) { if (args.pointType == PointInfoType.touchDown) { isFirstPointMove = false; isSecondPointMove = false; state = ItemStates.waiting; } } if (state == ItemStates.waiting) { switch (args.pointType) { case PointInfoType.touchDown: firstTouchStartPoint = args; // 设置线段起点 handleTouchBeforeStart(firstTouchStartPoint!); // 开始绘制前动态设置第一个起点 break; case PointInfoType.touchMove: isFirstPointMove = true; args.addOffset(0, -0.2); // 添加偏移 if (isMoveTargetOutOfRange(args)) return true; feature?.startPoint = args; feature?.endPoint = args; break; case PointInfoType.touchUp: if (isFirstPointMove) { args.addOffset(0, -0.2); // 添加偏移 } firstTouchStartPoint = args; // 设置线段起点 feature?.startPoint = args; state = ItemStates.running; break; default: break; } } else if (state == ItemStates.running) { switch (args.pointType) { case PointInfoType.touchDown: secondTouchStartPoint = args; break; case PointInfoType.touchMove: PointInfo newPoint = PointInfo.fromOffset( firstTouchStartPoint! .clone() .addVector(args - secondTouchStartPoint!) .toOffset(), args.pointType); newPoint.hostVisualArea = args.hostVisualArea; if (isMoveTargetOutOfRange(newPoint)) return true; isSecondPointMove = true; doCalculate(); feature?.endPoint = newPoint; break; case PointInfoType.touchUp: if (!isSecondPointMove) { feature?.endPoint = args; doCalculate(); } doFeatureFinish(); break; default: break; } } return true; } void handleMouseDownWhileWaiting(PointInfo args) { // TODO: 判断是否当前area // 转换为Area逻辑位置 final point = args.toAreaLogicPoint(); // feature = StraightLineFeature(this, point, point); if (args.hostVisualArea != null) { handleTissueTM(args.hostVisualArea!.mode.modeType, point); feature!.hostVisualArea = args.hostVisualArea; } state = ItemStates.running; } void handleTouchBeforeStart(PointInfo args) { // 转换为Area逻辑位置 final point = args.toAreaLogicPoint(); // feature = StraightLineFeature(this, point, point); if (args.hostVisualArea != null) { handleTissueTM(args.hostVisualArea!.mode.modeType, point); feature!.hostVisualArea = args.hostVisualArea; } } /// 处理TissueTimeMotion模式 void handleTissueTM(VidUsModeType mode, DPoint point) { if (mode == VidUsModeType.TissueTM || mode == VidUsModeType.Doppler) { switch (meta.measureType) { case MeasureTypes.TimeSpan: feature = StraightLineTimeMotionFeature(this, point, point, ifHorizontal: false); changeCrossIndicatorStyle(CrossIndicatorStyle.vertical); break; case MeasureTypes.VerticalDistance: feature = StraightLineTimeMotionFeature(this, point, point); changeCrossIndicatorStyle(CrossIndicatorStyle.horizontal); break; case MeasureTypes.Slope: feature = StraightLineSlopeFeature(this, point, point); break; case MeasureTypes.SlopeDoppler: feature = StraightLineSlopeFeature(this, point, point); break; case MeasureTypes.Pht: feature = StraightLinePhtFeature(this, point, point); changeCrossIndicatorStyle(CrossIndicatorStyle.vertical); break; case MeasureTypes.LvDpDt: feature = StraightLineSlopeFeature(this, point, point); break; default: } } else { if (meta.name.toLowerCase().contains("radius")) { feature = SemicircleFuture(this, point, point); } else { feature = StraightLineFeature(this, point, point); } } } void changeCrossIndicatorStyle(CrossIndicatorStyle e) { if (kIsMobile) { touchState.crossIndicatorStyleChanged.emit(this, e); } else { mouseState.crossIndicatorStyleChanged.emit(this, e); } } } class StraightLineFeature extends MeasureItemFeature { StraightLineFeature( IMeasureItem refItem, DPoint startPoint, DPoint endPoint, ) : super(refItem) { innerPoints.add(startPoint); innerPoints.add(endPoint); } /// 起点 DPoint get startPoint => innerPoints[0]; set startPoint(DPoint value) => innerPoints[0] = value; /// 终点 DPoint get endPoint => innerPoints[1]; set endPoint(DPoint value) => innerPoints[1] = value; /// 长度 double get length => (endPoint - startPoint).length; @override void paint(Canvas canvas, Size size) { if (startPoint == endPoint && kIsWeb) return; drawId(canvas, size, idText); final startOffset = convert2ViewPoint(size, startPoint).toOffset(); final endOffset = convert2ViewPoint(size, endPoint).toOffset(); final distanceOffset = endOffset - startOffset; if (distanceOffset.distance < measureData.getMinCursorDistance()) { drawMiniVertex(canvas, startOffset); drawMiniVertex(canvas, endOffset, isActive); } else { drawVertex(canvas, startOffset); drawVertex(canvas, endOffset, isActive); } canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan); } } class StraightLineTimeMotionFeature extends StraightLineFeature { StraightLineTimeMotionFeature( IMeasureItem refItem, DPoint startPoint, DPoint endPoint, {this.ifHorizontal = true}) : super(refItem, startPoint, endPoint); final bool ifHorizontal; @override void paint(Canvas canvas, Size size) { if (startPoint == endPoint && kIsWeb) return; drawId(canvas, size, idText); final startOffset = convert2ViewPoint(size, startPoint).toOffset(); final endOffset = convert2ViewPoint(size, endPoint).toOffset(); if (ifHorizontal) { drawMark(canvas, startOffset); drawMark(canvas, Offset(startOffset.dx, endOffset.dy), isActive); } else { drawMark(canvas, startOffset, false, ifHorizontal); drawMark( canvas, Offset(endOffset.dx, startOffset.dy), isActive, ifHorizontal); } } } class StraightLineSlopeFeature extends StraightLineFeature { StraightLineSlopeFeature( IMeasureItem refItem, DPoint startPoint, DPoint endPoint) : super(refItem, startPoint, endPoint); @override void paint(Canvas canvas, Size size) { if (startPoint == endPoint && kIsWeb) return; drawId(canvas, size, idText); final startOffset = convert2ViewPoint(size, startPoint).toOffset(); drawVertex(canvas, startOffset); final endOffset = convert2ViewPoint(size, endPoint).toOffset(); canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan); drawVertex(canvas, endOffset, isActive); } } class StraightLinePhtFeature extends StraightLineFeature { StraightLinePhtFeature( IMeasureItem refItem, DPoint startPoint, DPoint endPoint) : super(refItem, startPoint, endPoint); DPoint get regionCenter => hostVisualArea!.displayRegion.center; @override void paint(Canvas canvas, Size size) { if (startPoint == endPoint && kIsWeb) return; drawId(canvas, size, idText); final distanceToCenter = (startPoint.y - regionCenter.y); //TODO:[Gavin] 偏移量比例系数待精确 final endPointY = startPoint.y - distanceToCenter / 4; final startOffset = convert2ViewPoint(size, startPoint).toOffset(); final endOffset = convert2ViewPoint(size, DPoint(endPoint.x, endPointY)).toOffset(); drawVertex(canvas, startOffset, false); canvas.drawDashLine(startOffset, endOffset, 1, 10, paintLinePan); drawVertex(canvas, endOffset, isActive); } } /// 半圆 class SemicircleFuture extends StraightLineFeature { SemicircleFuture(super.refItem, super.startPoint, super.endPoint); @override void paint(Canvas canvas, Size size) { if (startPoint == endPoint && kIsWeb) return; drawId(canvas, size, idText); final center = convert2ViewPoint(size, startPoint).toOffset(); final activeOffset = convert2ViewPoint(size, endPoint).toOffset(); final distanceOffset = activeOffset - center; // 计算半径 final radius = distanceOffset.distance; // 活动点到圆心,在X轴正方向的角度 final activeAngle = math.atan2(distanceOffset.dy, distanceOffset.dx); // 半圆其实点,activeAngle向前90°角 final startAngle = activeAngle - math.pi * 0.5; // 半圆 const sweepAngle = math.pi; // 绘制半圆 canvas.drawArc( Rect.fromCircle(center: center, radius: radius), startAngle, sweepAngle, false, paintLinePan, ); } }