import 'dart:ui'; 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/terms.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/curve.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'; import 'package:fis_measure/process/primitives/utils/auto_snap.dart'; import 'package:fis_measure/utils/canvas.dart'; import 'curve_abstract.dart'; /// 折线多边形 class Polyline extends CurveAbstract with AutoSnapMixin { Polyline(ItemMeta meta, IMeasureItem? parent) : super(meta, parent); static Polyline createAreaPerimeter(ItemMeta meta, [IMeasureItem? parent]) { Polyline polygon = Polyline(meta, parent); polygon.calculator = _AreaPerimeterCalc(polygon); return polygon; } static Polyline createCurveLength(ItemMeta meta, [IMeasureItem? parent]) { Polyline polygon = Polyline(meta, parent); polygon.calculator = CurveLengthCal(polygon); polygon.isClosed = false; 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); } else { f.innerPoints.last = args; } doCalculate(); checkAutoFinish(args); // if (f.startPoint.almostEquals(f.endPoint)) { // doFeatureFinish(); // } } return true; } @override bool onExecuteTouch(PointInfo args) { // TODO: implement onExecuteTouch throw UnimplementedError(); } void handleMouseDownWhileWaiting(PointInfo args) { // TODO: 判断是否当前area // 转换为Area逻辑位置 final point = args.toAreaLogicPoint(); feature = PolylineFeature(this, point); if (args.hostVisualArea != null) { feature!.hostVisualArea = args.hostVisualArea; } state = ItemStates.running; } } class PolylineFeature extends CurveAbstractFeature { PolylineFeature(CurveAbstract refItem, DPoint point) : super(refItem) { innerPoints.add(point.clone()); innerPoints.add(point.clone()); } @override void paint(Canvas canvas, Size size) { if (innerPoints.isEmpty) return; drawId(canvas, size); final startOffset = convert2ViewPoint(size, startPoint).toOffset(); if (innerPoints.length == 1) { drawVertex(canvas, startOffset, true); return; } else { drawVertex(canvas, startOffset); } final len = innerPoints.length; for (var i = 1; i < len; i++) { final a = innerPoints[i - 1]; final b = innerPoints[i]; final offsetA = convert2ViewPoint(size, a).toOffset(); final offsetB = convert2ViewPoint(size, b).toOffset(); canvas.drawDashLine(offsetA, offsetB, 1, 10, paintPan); final isLast = len - i == 1; if (isLast) { drawVertex(canvas, offsetB, isActive); if (isClosed) { canvas.drawDashLine(offsetB, startOffset, 1, 10, paintPan); } } else { drawVertex(canvas, offsetB, false); } } } } class _AreaPerimeterCalc extends Calculator { _AreaPerimeterCalc(Polyline 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 area; double perimeter; if ((feature.splineTension - 0).abs() > 0) { // TODO: CreateSpline - Polyline.cs 850 perimeter = 0; area = 0; } else { double threshold = 0.0; area = calcArea(points); perimeter = calcPerimeter(points, feature.isClosed, threshold); } for (var output in ref.meta.outputs) { if (output.name == MeasureTerms.Perimeter) { var value = roundDouble(perimeter, output.fractionalDigits); feature.updateFloatValue(output, value, output.unit); } else if (output.name == MeasureTerms.Area) { var value = roundDouble(area, output.fractionalDigits); feature.updateFloatValue(output, value, output.unit); } } } static double calcArea(List points) { if (points.isEmpty) { return 0; } double sum = 0; var ax = points[0].x; var ay = points[0].y; for (var i = 1; i < points.length - 1; i++) { var bx = points[i].x; var by = points[i].y; var cx = points[i + 1].x; var cy = points[i + 1].y; sum += ax * by - ay * bx + ay * cx - ax * cy + bx * cy - cx * by; } return (-sum / 2).abs(); } static double calcPerimeter( List points, bool isClosed, double threshold, ) { final len = points.length; if (points.length < 2) { return 0; } double sum = 0; for (int i = 1, j = 0; j < len && i < len; i++) { var length = (points[i] - points[j]).length; if ((length).abs() > threshold) { sum += length; j = i; } } if (isClosed) { sum += (points[len - 1] - points[0]).length; } return sum; } }