|
@@ -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;
|
|
|
+
|
|
|
+ } 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) {
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+ throw UnimplementedError();
|
|
|
+ }
|
|
|
+
|
|
|
+ void handleMouseDownWhileWaiting(PointInfo args) {
|
|
|
+
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+ _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();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @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);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+ recreateSpline(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void fixedSpline() {
|
|
|
+ return;
|
|
|
+ if (innerPoints.isEmpty) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ DPoint((innerPoints.last.x + innerPoints.first.x) * 0.5,
|
|
|
+ (innerPoints.last.y + innerPoints.first.y) * 0.5);
|
|
|
+
|
|
|
+ var movablePoint = DPoint(0, double.infinity);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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,
|
|
|
+ ) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 = <DPoint>[];
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var pathGeometry = Path();
|
|
|
+
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+}
|