|
@@ -0,0 +1,457 @@
|
|
|
+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;
|
|
|
+ }
|
|
|
+}
|