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.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/formulas/general.dart'; import 'package:fis_measure/process/items/item.dart'; import 'package:fis_measure/process/items/item_feature.dart'; import 'package:fis_measure/process/primitives/polyline.dart'; import 'package:fis_measure/process/primitives/area_abstract.dart'; import 'package:fis_measure/process/primitives/utils/auto_snap.dart'; import 'package:fis_measure/utils/canvas.dart'; import 'package:flutter/foundation.dart'; import 'package:path_drawing/path_drawing.dart'; enum LvSimpsonStep { none, splineBeginEdit, splineEditing, splineEndEdit, splineCompleted, done, } class SimpsonPath extends AreaItemAbstract with AutoSnapMixin { static const int SplitterCount = 20; static const double MaxPointsCount = 10000; SimpsonPath(ItemMeta meta, IMeasureItem? parent) : super(meta, parent); PointInfo? firstPoint; @protected LvSimpsonStep lvSimpsonStep = LvSimpsonStep.none; @override SimpsonPathFeature? get feature => super.feature as SimpsonPathFeature; @override bool onExecuteMouse(PointInfo args) { if (state == ItemStates.finished) { if (args.pointType == PointInfoType.mouseDown) { state = ItemStates.waiting; lvSimpsonStep = LvSimpsonStep.none; } } if (state == ItemStates.waiting) { if (args.pointType == PointInfoType.mouseDown) { handleMouseDownWhileWaiting(args); } } else if (state == ItemStates.running) { switch (args.pointType) { case PointInfoType.mouseUp: return false; case PointInfoType.mouseDown: if (lvSimpsonStep == LvSimpsonStep.splineCompleted) { feature!.adjustEndPoint(args.toAreaLogicPoint()); lvSimpsonStep = LvSimpsonStep.done; // CaliperExtension.ShowCaliper(); } else { final lineLen = (args - firstPoint!).length; if ((feature!.innerPoints.length > 3 && GeneralFormulas.doubleAlmostEquals(lineLen, 0)) || feature!.innerPoints.length >= MaxPointsCount) { lvSimpsonStep = LvSimpsonStep.splineCompleted; feature!.fixedSpline(); } else { feature!.adopt(args.toAreaLogicPoint()); } } break; case PointInfoType.mouseMove: if (lvSimpsonStep == LvSimpsonStep.splineCompleted) { feature!.adjustEndPoint(args.toAreaLogicPoint()); } else { if (!snapState) { checkAutoSnap(args, false); } if (!snapState) { feature!.updateActivePoint(args.toAreaLogicPoint()); } } break; default: break; } } doCalculate(); if (args.pointType == PointInfoType.mouseDown) { // doFeatureFinish(); if (state == ItemStates.waiting || state == ItemStates.finished) { state = ItemStates.running; } if (state == ItemStates.running) { updateStatus(); } } if (args.pointType == PointInfoType.mouseMove) { updateStatus(); } return true; } @override bool onExecuteTouch(PointInfo args) { // TODO: implement onExecuteTouch throw UnimplementedError(); } void handleMouseDownWhileWaiting(PointInfo args) { // TODO: 判断是否当前area // 转换为Area逻辑位置 feature = SimpsonPathFeature(this); if (args.hostVisualArea != null) { feature!.hostVisualArea = args.hostVisualArea; } final point = args.toAreaLogicPoint(); feature!.adopt(point); feature!.adopt(point); state = ItemStates.running; firstPoint = args; } void updateStatus() { switch (lvSimpsonStep) { case LvSimpsonStep.splineCompleted: firstPoint = null; if (feature != null) { feature!.activeIndex = feature!.innerPoints.length; } break; case LvSimpsonStep.done: state = ItemStates.finished; if (feature != null) { feature!.activeIndex = -1; } break; default: break; } } static SimpsonPath create(ItemMeta meta, [IMeasureItem? parent]) { final path = SimpsonPath(meta, parent); return path; } } class SimpsonPathFeature extends AreaItemFeatureAbstract { Map<int, double> horizontalSplitterLegths = {}; IPathGeometry? _pathGeometry; List<DPoint>? _splinePoints; IPathGeometry? _spline; late DPoint _centerLineFixedPoint; late DPoint _centerLineMovablePoint; late DPoint _leftPoint; late DPoint _rightPoint; late DPoint _apexPoint; SimpsonPathFeature(AreaItemAbstract refItem) : super(refItem) { // _splinePoints = []; _centerLineFixedPoint = DPointExt.empty; _centerLineMovablePoint = DPointExt.empty; _leftPoint = DPointExt.empty; _rightPoint = DPointExt.empty; _apexPoint = DPointExt.empty; } @override SimpsonPath get refItem => super.refItem as SimpsonPath; DPoint get centerLineMovablePoint => _centerLineMovablePoint; set centerLineMovablePoint(DPoint val) { if (val != _centerLineMovablePoint) { _centerLineMovablePoint = val; updateSplitters(); // OnVertexPointChanged(); } } @override void paint(Canvas canvas, Size size) { if (innerPoints.isEmpty) return; drawId(canvas, size); final points = innerPoints.map((e) => convert2ViewPoint(size, e)).toList(); final startPoint = points.first; drawVertex(canvas, startPoint.toOffset(), points.length == 1); // if (points.length > 1) { // final Path path = Path(); // path.moveTo(startPoint.x, startPoint.y); // for (var i = 1; i < points.length; i++) { // final point = points[i]; // path.lineTo(point.x, point.y); // } // if (isClosed) { // path.lineTo(startPoint.x, startPoint.y); // } // canvas.drawPath( // path, // paintLinePan, // ); // } drawVertex(canvas, points.last.toOffset(), isActive); } @override void adopt(DPoint point) { super.adopt(point); recreateSpline(false); } void updateActivePoint(DPoint point) { if (activeIndex > 0 && activeIndex <= innerPoints.length) { // ActivePoint = point; recreateSpline(false); } } void fixedSpline() { return; if (innerPoints.isEmpty) { return; } // _centerLineFixedPoint = DPoint((innerPoints.last.x + innerPoints.first.x) * 0.5, (innerPoints.last.y + innerPoints.first.y) * 0.5); var movablePoint = DPoint(0, double.infinity); // if (BaseType == MeasureTypes.SimpsonAutoTrace) // { // movablePoint = InnerPoints[InnerPoints.Count >> 1]; // } // else // { // var isFindMin = innerPoints[innerPoints.length >> 1].y - innerPoints[0].y < 0; // if (AutoGetApexPoint) // { // movablePoint = isFindMin ? movablePoint : DPoint(0, double.negativeInfinity); // } // foreach (DPoint point in InnerPoints) // { // if (AutoGetApexPoint && innerPoints.Count > 0) // { // if (isFindMin) // { // if (point.Y < movablePoint.Y) // { // movablePoint = point; // } // } // else // { // if (point.Y > movablePoint.Y) // { // movablePoint = point; // } // } // } // else // { // if (point.Y < movablePoint.Y) // { // movablePoint = point; // } // } // } // } // if (_centerLineMovablePoint != movablePoint) // { // _centerLineMovablePoint = movablePoint; // if (!DPointExtension.IsEmpty(_centerLineMovablePoint) && (BaseType != MeasureTypes.SimpsonAutoTrace)) // { // _centerLineMovablePoint.SynchToMainMonitorScreen(HostArea); // } // } // UpdateSplitters(); // OnVertexPointChanged(); } void adjustEndPoint(DPoint point) { if (innerPoints.isEmpty) { return; } var endPoint = point; double minDistance = double.infinity; if (_splinePoints != null) { for (DPoint splinePoint in _splinePoints!) { double distance = (point - splinePoint).length; if (distance < minDistance) { minDistance = distance; endPoint = splinePoint; } } } centerLineMovablePoint = endPoint; } void recreateSpline(bool isClosed) { // if (_breaker.Paused) { // return; // } final generator = SimpsonGeometryGenerator(); _pathGeometry ??= generator.createPathGeometry(); double tempCircumference = 0; _spline = generator.createSpline( innerPoints, 0.5, null, isClosed, false, 0.005, tempCircumference, _splinePoints, ); if (_spline != null && _splinePoints != null) { _pathGeometry!.addGeometry(_spline!); } } void updateSplitters() { recreateSpline(true); if (_spline != null && !_centerLineMovablePoint.isEmpty) { var generator = SimpsonGeometryGenerator(); _pathGeometry!.clear(); if (innerPoints.isNotEmpty) { _pathGeometry!.addGeometry(_spline!); horizontalSplitterLegths = {}; generator.createSplitters( _pathGeometry!, _spline!, _centerLineFixedPoint, _centerLineMovablePoint, horizontalSplitterLegths, ); _pathGeometry!.addLineGeometry( _centerLineFixedPoint, _centerLineMovablePoint, ); } } } } class SimpsonGeometryGenerator { static const int splitterCount = 20; void createSplitters( IPathGeometry pathGeometry, IPathGeometry spline, DPoint centerLineFixedPoint, DPoint centerLineMovablePoint, Map<int, double> horizontalSplitterLegths, ) { // TODO: 辛普森绘制核心 } IPathGeometry? createSpline( List<DPoint> sourcePoints, double tension, List<double>? tensions, bool isClosed, bool isFilled, double tolerance, double length, List<DPoint>? splinePoints, ) { length = 0; if (sourcePoints.isEmpty) { return null; } List<DPoint> samplePoints = []; // var points = Spline.Create(sourcePoints, tension, tensions, isClosed, tolerance, true, out length, samplePoints,true); var points = <DPoint>[]; // var myPoints = GeomTools.ToWindowPoints(points.ToArray()); // var polyLineSegment = new PolyLineSegment { Points = new PointCollection(myPoints) }; // var pathFigure = new PathFigure { IsClosed = isClosed, IsFilled = true, StartPoint = sourcePoints[0].ToWindowPoint() }; // pathFigure.Segments.Add(polyLineSegment); var pathGeometry = Path(); // pathGeometry.Figures.Add(pathFigure); splinePoints = points; return PathGeometryContainer(pathGeometry); } IPathGeometry createPathGeometry() { return PathGeometryContainer(Path()); } } class PathGeometryContainer implements IPathGeometry { final pathList = <Path>[]; late final Path control; PathGeometryContainer(Path geometry) { control = geometry; } @override void addGeometry(IPathGeometry geometry) { // TODO: // pathList.addAll(geometry.pathList); } void addPath(Path path) { pathList.add(path); } @override void clear() { pathList.clear(); } @override void addLineGeometry( DPoint centerLineFixedPoint, DPoint centerLineMovablePoint) { final linePath = Path() ..moveTo(centerLineFixedPoint.x, centerLineFixedPoint.y) ..lineTo(centerLineMovablePoint.x, centerLineMovablePoint.y); addPath(linePath); } } abstract class IPathGeometry { void clear(); void addGeometry(IPathGeometry spline); void addLineGeometry( DPoint centerLineFixedPoint, DPoint centerLineMovablePoint); } extension DPointExt on DPoint { static final empty = DPoint(double.minPositive, double.minPositive); bool get isEmpty { return this == empty; } }