simpson_path.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. import 'dart:ui';
  2. import 'package:fis_measure/interfaces/date_types/point.dart';
  3. import 'package:fis_measure/interfaces/enums/items.dart';
  4. import 'package:fis_measure/interfaces/process/items/item.dart';
  5. import 'package:fis_measure/interfaces/process/items/item_metas.dart';
  6. import 'package:fis_measure/interfaces/process/items/types.dart';
  7. import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
  8. import 'package:fis_measure/process/calcuators/formulas/general.dart';
  9. import 'package:fis_measure/process/items/item.dart';
  10. import 'package:fis_measure/process/items/item_feature.dart';
  11. import 'package:fis_measure/process/primitives/polyline.dart';
  12. import 'package:fis_measure/process/primitives/area_abstract.dart';
  13. import 'package:fis_measure/process/primitives/utils/auto_snap.dart';
  14. import 'package:fis_measure/utils/canvas.dart';
  15. import 'package:flutter/foundation.dart';
  16. import 'package:path_drawing/path_drawing.dart';
  17. enum LvSimpsonStep {
  18. none,
  19. splineBeginEdit,
  20. splineEditing,
  21. splineEndEdit,
  22. splineCompleted,
  23. done,
  24. }
  25. class SimpsonPath extends AreaItemAbstract with AutoSnapMixin {
  26. static const int SplitterCount = 20;
  27. static const double MaxPointsCount = 10000;
  28. SimpsonPath(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
  29. PointInfo? firstPoint;
  30. @protected
  31. LvSimpsonStep lvSimpsonStep = LvSimpsonStep.none;
  32. @override
  33. SimpsonPathFeature? get feature => super.feature as SimpsonPathFeature;
  34. @override
  35. bool onExecuteMouse(PointInfo args) {
  36. if (state == ItemStates.finished) {
  37. if (args.pointType == PointInfoType.mouseDown) {
  38. state = ItemStates.waiting;
  39. lvSimpsonStep = LvSimpsonStep.none;
  40. }
  41. }
  42. if (state == ItemStates.waiting) {
  43. if (args.pointType == PointInfoType.mouseDown) {
  44. handleMouseDownWhileWaiting(args);
  45. }
  46. } else if (state == ItemStates.running) {
  47. switch (args.pointType) {
  48. case PointInfoType.mouseUp:
  49. return false;
  50. case PointInfoType.mouseDown:
  51. if (lvSimpsonStep == LvSimpsonStep.splineCompleted) {
  52. feature!.adjustEndPoint(args.toAreaLogicPoint());
  53. lvSimpsonStep = LvSimpsonStep.done;
  54. // CaliperExtension.ShowCaliper();
  55. } else {
  56. final lineLen = (args - firstPoint!).length;
  57. if ((feature!.innerPoints.length > 3 &&
  58. GeneralFormulas.doubleAlmostEquals(lineLen, 0)) ||
  59. feature!.innerPoints.length >= MaxPointsCount) {
  60. lvSimpsonStep = LvSimpsonStep.splineCompleted;
  61. feature!.fixedSpline();
  62. } else {
  63. feature!.adopt(args.toAreaLogicPoint());
  64. }
  65. }
  66. break;
  67. case PointInfoType.mouseMove:
  68. if (lvSimpsonStep == LvSimpsonStep.splineCompleted) {
  69. feature!.adjustEndPoint(args.toAreaLogicPoint());
  70. } else {
  71. if (!snapState) {
  72. checkAutoSnap(args, false);
  73. }
  74. if (!snapState) {
  75. feature!.updateActivePoint(args.toAreaLogicPoint());
  76. }
  77. }
  78. break;
  79. default:
  80. break;
  81. }
  82. }
  83. doCalculate();
  84. if (args.pointType == PointInfoType.mouseDown) {
  85. // doFeatureFinish();
  86. if (state == ItemStates.waiting || state == ItemStates.finished) {
  87. state = ItemStates.running;
  88. }
  89. if (state == ItemStates.running) {
  90. updateStatus();
  91. }
  92. }
  93. if (args.pointType == PointInfoType.mouseMove) {
  94. updateStatus();
  95. }
  96. return true;
  97. }
  98. @override
  99. bool onExecuteTouch(PointInfo args) {
  100. // TODO: implement onExecuteTouch
  101. throw UnimplementedError();
  102. }
  103. void handleMouseDownWhileWaiting(PointInfo args) {
  104. // TODO: 判断是否当前area
  105. // 转换为Area逻辑位置
  106. feature = SimpsonPathFeature(this);
  107. if (args.hostVisualArea != null) {
  108. feature!.hostVisualArea = args.hostVisualArea;
  109. }
  110. final point = args.toAreaLogicPoint();
  111. feature!.adopt(point);
  112. feature!.adopt(point);
  113. state = ItemStates.running;
  114. firstPoint = args;
  115. }
  116. void updateStatus() {
  117. switch (lvSimpsonStep) {
  118. case LvSimpsonStep.splineCompleted:
  119. firstPoint = null;
  120. if (feature != null) {
  121. feature!.activeIndex = feature!.innerPoints.length;
  122. }
  123. break;
  124. case LvSimpsonStep.done:
  125. state = ItemStates.finished;
  126. if (feature != null) {
  127. feature!.activeIndex = -1;
  128. }
  129. break;
  130. default:
  131. break;
  132. }
  133. }
  134. static SimpsonPath create(ItemMeta meta, [IMeasureItem? parent]) {
  135. final path = SimpsonPath(meta, parent);
  136. return path;
  137. }
  138. }
  139. class SimpsonPathFeature extends AreaItemFeatureAbstract {
  140. Map<int, double> horizontalSplitterLegths = {};
  141. IPathGeometry? _pathGeometry;
  142. List<DPoint>? _splinePoints;
  143. IPathGeometry? _spline;
  144. late DPoint _centerLineFixedPoint;
  145. late DPoint _centerLineMovablePoint;
  146. late DPoint _leftPoint;
  147. late DPoint _rightPoint;
  148. late DPoint _apexPoint;
  149. SimpsonPathFeature(AreaItemAbstract refItem) : super(refItem) {
  150. // _splinePoints = [];
  151. _centerLineFixedPoint = DPointExt.empty;
  152. _centerLineMovablePoint = DPointExt.empty;
  153. _leftPoint = DPointExt.empty;
  154. _rightPoint = DPointExt.empty;
  155. _apexPoint = DPointExt.empty;
  156. }
  157. @override
  158. SimpsonPath get refItem => super.refItem as SimpsonPath;
  159. DPoint get centerLineMovablePoint => _centerLineMovablePoint;
  160. set centerLineMovablePoint(DPoint val) {
  161. if (val != _centerLineMovablePoint) {
  162. _centerLineMovablePoint = val;
  163. updateSplitters();
  164. // OnVertexPointChanged();
  165. }
  166. }
  167. @override
  168. void paint(Canvas canvas, Size size) {
  169. if (innerPoints.isEmpty) return;
  170. drawId(canvas, size);
  171. final points = innerPoints.map((e) => convert2ViewPoint(size, e)).toList();
  172. final startPoint = points.first;
  173. drawVertex(canvas, startPoint.toOffset(), points.length == 1);
  174. // if (points.length > 1) {
  175. // final Path path = Path();
  176. // path.moveTo(startPoint.x, startPoint.y);
  177. // for (var i = 1; i < points.length; i++) {
  178. // final point = points[i];
  179. // path.lineTo(point.x, point.y);
  180. // }
  181. // if (isClosed) {
  182. // path.lineTo(startPoint.x, startPoint.y);
  183. // }
  184. // canvas.drawPath(
  185. // path,
  186. // paintLinePan,
  187. // );
  188. // }
  189. drawVertex(canvas, points.last.toOffset(), isActive);
  190. }
  191. @override
  192. void adopt(DPoint point) {
  193. super.adopt(point);
  194. recreateSpline(false);
  195. }
  196. void updateActivePoint(DPoint point) {
  197. if (activeIndex > 0 && activeIndex <= innerPoints.length) {
  198. // ActivePoint = point;
  199. recreateSpline(false);
  200. }
  201. }
  202. void fixedSpline() {
  203. return;
  204. if (innerPoints.isEmpty) {
  205. return;
  206. }
  207. // _centerLineFixedPoint =
  208. DPoint((innerPoints.last.x + innerPoints.first.x) * 0.5,
  209. (innerPoints.last.y + innerPoints.first.y) * 0.5);
  210. var movablePoint = DPoint(0, double.infinity);
  211. // if (BaseType == MeasureTypes.SimpsonAutoTrace)
  212. // {
  213. // movablePoint = InnerPoints[InnerPoints.Count >> 1];
  214. // }
  215. // else
  216. // {
  217. // var isFindMin = innerPoints[innerPoints.length >> 1].y - innerPoints[0].y < 0;
  218. // if (AutoGetApexPoint)
  219. // {
  220. // movablePoint = isFindMin ? movablePoint : DPoint(0, double.negativeInfinity);
  221. // }
  222. // foreach (DPoint point in InnerPoints)
  223. // {
  224. // if (AutoGetApexPoint && innerPoints.Count > 0)
  225. // {
  226. // if (isFindMin)
  227. // {
  228. // if (point.Y < movablePoint.Y)
  229. // {
  230. // movablePoint = point;
  231. // }
  232. // }
  233. // else
  234. // {
  235. // if (point.Y > movablePoint.Y)
  236. // {
  237. // movablePoint = point;
  238. // }
  239. // }
  240. // }
  241. // else
  242. // {
  243. // if (point.Y < movablePoint.Y)
  244. // {
  245. // movablePoint = point;
  246. // }
  247. // }
  248. // }
  249. // }
  250. // if (_centerLineMovablePoint != movablePoint)
  251. // {
  252. // _centerLineMovablePoint = movablePoint;
  253. // if (!DPointExtension.IsEmpty(_centerLineMovablePoint) && (BaseType != MeasureTypes.SimpsonAutoTrace))
  254. // {
  255. // _centerLineMovablePoint.SynchToMainMonitorScreen(HostArea);
  256. // }
  257. // }
  258. // UpdateSplitters();
  259. // OnVertexPointChanged();
  260. }
  261. void adjustEndPoint(DPoint point) {
  262. if (innerPoints.isEmpty) {
  263. return;
  264. }
  265. var endPoint = point;
  266. double minDistance = double.infinity;
  267. if (_splinePoints != null) {
  268. for (DPoint splinePoint in _splinePoints!) {
  269. double distance = (point - splinePoint).length;
  270. if (distance < minDistance) {
  271. minDistance = distance;
  272. endPoint = splinePoint;
  273. }
  274. }
  275. }
  276. centerLineMovablePoint = endPoint;
  277. }
  278. void recreateSpline(bool isClosed) {
  279. // if (_breaker.Paused) {
  280. // return;
  281. // }
  282. final generator = SimpsonGeometryGenerator();
  283. _pathGeometry ??= generator.createPathGeometry();
  284. double tempCircumference = 0;
  285. _spline = generator.createSpline(
  286. innerPoints,
  287. 0.5,
  288. null,
  289. isClosed,
  290. false,
  291. 0.005,
  292. tempCircumference,
  293. _splinePoints,
  294. );
  295. if (_spline != null && _splinePoints != null) {
  296. _pathGeometry!.addGeometry(_spline!);
  297. }
  298. }
  299. void updateSplitters() {
  300. recreateSpline(true);
  301. if (_spline != null && !_centerLineMovablePoint.isEmpty) {
  302. var generator = SimpsonGeometryGenerator();
  303. _pathGeometry!.clear();
  304. if (innerPoints.isNotEmpty) {
  305. _pathGeometry!.addGeometry(_spline!);
  306. horizontalSplitterLegths = {};
  307. generator.createSplitters(
  308. _pathGeometry!,
  309. _spline!,
  310. _centerLineFixedPoint,
  311. _centerLineMovablePoint,
  312. horizontalSplitterLegths,
  313. );
  314. _pathGeometry!.addLineGeometry(
  315. _centerLineFixedPoint,
  316. _centerLineMovablePoint,
  317. );
  318. }
  319. }
  320. }
  321. }
  322. class SimpsonGeometryGenerator {
  323. static const int splitterCount = 20;
  324. void createSplitters(
  325. IPathGeometry pathGeometry,
  326. IPathGeometry spline,
  327. DPoint centerLineFixedPoint,
  328. DPoint centerLineMovablePoint,
  329. Map<int, double> horizontalSplitterLegths,
  330. ) {
  331. // TODO: 辛普森绘制核心
  332. }
  333. IPathGeometry? createSpline(
  334. List<DPoint> sourcePoints,
  335. double tension,
  336. List<double>? tensions,
  337. bool isClosed,
  338. bool isFilled,
  339. double tolerance,
  340. double length,
  341. List<DPoint>? splinePoints,
  342. ) {
  343. length = 0;
  344. if (sourcePoints.isEmpty) {
  345. return null;
  346. }
  347. List<DPoint> samplePoints = [];
  348. // var points = Spline.Create(sourcePoints, tension, tensions, isClosed, tolerance, true, out length, samplePoints,true);
  349. var points = <DPoint>[];
  350. // var myPoints = GeomTools.ToWindowPoints(points.ToArray());
  351. // var polyLineSegment = new PolyLineSegment { Points = new PointCollection(myPoints) };
  352. // var pathFigure = new PathFigure { IsClosed = isClosed, IsFilled = true, StartPoint = sourcePoints[0].ToWindowPoint() };
  353. // pathFigure.Segments.Add(polyLineSegment);
  354. var pathGeometry = Path();
  355. // pathGeometry.Figures.Add(pathFigure);
  356. splinePoints = points;
  357. return PathGeometryContainer(pathGeometry);
  358. }
  359. IPathGeometry createPathGeometry() {
  360. return PathGeometryContainer(Path());
  361. }
  362. }
  363. class PathGeometryContainer implements IPathGeometry {
  364. final pathList = <Path>[];
  365. late final Path control;
  366. PathGeometryContainer(Path geometry) {
  367. control = geometry;
  368. }
  369. @override
  370. void addGeometry(IPathGeometry geometry) {
  371. // TODO:
  372. // pathList.addAll(geometry.pathList);
  373. }
  374. void addPath(Path path) {
  375. pathList.add(path);
  376. }
  377. @override
  378. void clear() {
  379. pathList.clear();
  380. }
  381. @override
  382. void addLineGeometry(
  383. DPoint centerLineFixedPoint, DPoint centerLineMovablePoint) {
  384. final linePath = Path()
  385. ..moveTo(centerLineFixedPoint.x, centerLineFixedPoint.y)
  386. ..lineTo(centerLineMovablePoint.x, centerLineMovablePoint.y);
  387. addPath(linePath);
  388. }
  389. }
  390. abstract class IPathGeometry {
  391. void clear();
  392. void addGeometry(IPathGeometry spline);
  393. void addLineGeometry(
  394. DPoint centerLineFixedPoint, DPoint centerLineMovablePoint);
  395. }
  396. extension DPointExt on DPoint {
  397. static final empty = DPoint(double.minPositive, double.minPositive);
  398. bool get isEmpty {
  399. return this == empty;
  400. }
  401. }