item_feature.dart 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import 'package:fis_common/logger/logger.dart';
  2. import 'package:fis_measure/interfaces/date_types/point.dart';
  3. import 'package:fis_measure/interfaces/process/calculators/values.dart';
  4. import 'package:fis_measure/interfaces/process/items/item.dart';
  5. import 'package:fis_measure/interfaces/process/items/item_feature.dart';
  6. import 'package:fis_measure/interfaces/process/items/item_metas.dart';
  7. import 'package:fis_measure/interfaces/process/visuals/visual_area.dart';
  8. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  9. import 'package:fis_measure/utils/canvas.dart';
  10. import 'package:fis_measure/values/colors.dart';
  11. import 'package:flutter/widgets.dart';
  12. import 'package:get/get.dart';
  13. import 'package:vid/us/vid_us_unit.dart';
  14. import 'item.dart';
  15. abstract class MeasureItemFeature implements IMeasureItemFeature {
  16. late IMeasureItem _refItem;
  17. late List<DPoint> _innerPoints;
  18. late FeatureStyle _featureStyle;
  19. late String _measureModeName;
  20. IVisualArea? _hostVisualArea;
  21. bool _isActive = true;
  22. int _id = 0;
  23. List<ValueBase> _values = [];
  24. late final measureData = Get.find<MeasureDataController>();
  25. int _activeIndex = -1;
  26. Rect? zoomRect; // 当前缩放区域的归一化坐标
  27. @override
  28. int? frameIndex;
  29. @override
  30. int? imageBelongSign;
  31. @protected
  32. final paintPan = Paint()
  33. ..color = MeasureColors.Primary
  34. ..isAntiAlias = true
  35. ..strokeWidth = 2
  36. ..style = PaintingStyle.stroke;
  37. Paint get paintLinePan => measureData.paintLinePan;
  38. Paint get paintPointPan => measureData.paintPointPan;
  39. double get annotationFontSize =>
  40. measureData.measureSystemSetting.annotationFontSize.toDouble();
  41. MeasureItemFeature(IMeasureItem refItem) {
  42. _refItem = refItem;
  43. if (refItem.parent != null) {
  44. _id = refItem.parent!.feature?.id ?? 0;
  45. } else {
  46. _id = refItem.assignId();
  47. }
  48. _innerPoints = [];
  49. _measureModeName = "";
  50. _recordHistory();
  51. try {
  52. final measureData = Get.find<MeasureDataController>();
  53. _featureStyle = FeatureStyle(
  54. showBriefAnnotation:
  55. measureData.measureSystemSetting.showBriefAnnotation);
  56. } catch (e) {
  57. logger.e("Init meature item's feature style failed: $e");
  58. }
  59. }
  60. int get activeIndex => _activeIndex;
  61. set activeIndex(int val) {
  62. if (val != _activeIndex) {
  63. _activeIndex = val;
  64. }
  65. }
  66. @override
  67. FeatureStyle get featureStyle => _featureStyle;
  68. @override
  69. List<DPoint> get innerPoints => _innerPoints;
  70. @override
  71. MeasureItem get refItem => _refItem as MeasureItem;
  72. @override
  73. String get measureModeName => _measureModeName;
  74. /// 所在区域
  75. IVisualArea? get hostVisualArea => _hostVisualArea;
  76. set hostVisualArea(IVisualArea? value) {
  77. if (value != _hostVisualArea) {
  78. _hostVisualArea = value;
  79. }
  80. }
  81. @override
  82. bool get isActive => _isActive;
  83. set isActive(bool value) {
  84. if (value != _isActive) {
  85. _isActive = value;
  86. }
  87. }
  88. @override
  89. int get id => _id;
  90. /// 获取画布轨迹起点的角标文案,如果测量结果显示简洁注释,则会在轨迹起点显示简洁注释
  91. String get idText => _getIdText();
  92. String _getIdText() {
  93. if (featureStyle.showBriefAnnotation) {
  94. if (refItem.briefAnnotation.isNotEmpty) {
  95. return '$id.${refItem.briefAnnotation}';
  96. }
  97. }
  98. return '$id';
  99. }
  100. /// 顶点尺寸
  101. double get vertexSize =>
  102. measureData.measureSystemSetting.shapeCursorSize * refItem.scaleRatio;
  103. @override
  104. List<ValueBase> get values => _values;
  105. @override
  106. ValueBase? get value => _values.isNotEmpty ? _values.first : null;
  107. /// 更新浮点型结果值
  108. FloatValue? updateFloatValue(
  109. ItemOutputMeta outputMeta,
  110. double value,
  111. VidUsUnit unit,
  112. ) {
  113. int index = values.indexWhere((e) => e.meta.name == outputMeta.name);
  114. if (index < 0) {
  115. final floatValue = FloatValue(outputMeta, value, unit);
  116. values.add(floatValue);
  117. return floatValue;
  118. } else {
  119. ValueBase valueBase = values[index];
  120. if (valueBase is FloatValue) {
  121. final floatValue = valueBase;
  122. floatValue.value = value;
  123. return floatValue;
  124. } else {
  125. final newValue = FloatValue(outputMeta, value, unit);
  126. values[index] = newValue;
  127. return newValue;
  128. }
  129. }
  130. }
  131. void initValues() {
  132. _values = [];
  133. }
  134. /// 更新字符串结果值
  135. StringValue updateStringValue(
  136. ItemOutputMeta outputMeta,
  137. String value, [
  138. VidUsUnit unit = VidUsUnit.None,
  139. ]) {
  140. int index = values.indexWhere((e) => e.meta.name == outputMeta.name);
  141. if (index < 0) {
  142. final stringValue = StringValue(outputMeta, value, unit);
  143. values.add(stringValue);
  144. return stringValue;
  145. } else {
  146. ValueBase valueBase = values[index];
  147. if (valueBase is StringValue) {
  148. final floatValue = valueBase;
  149. floatValue.value = value;
  150. return floatValue;
  151. } else {
  152. final newValue = StringValue(outputMeta, value, unit);
  153. values[index] = newValue;
  154. return newValue;
  155. }
  156. }
  157. }
  158. @protected
  159. DPoint convert2ViewPoint(Size size, DPoint logicPoint) {
  160. if (zoomRect != null) {
  161. DPoint zoomedLogicPoint = getZoomedPoint(zoomRect!, logicPoint);
  162. final x = size.width * zoomedLogicPoint.x;
  163. final y = size.height * zoomedLogicPoint.y;
  164. return DPoint(x, y);
  165. }
  166. final x = size.width * logicPoint.x;
  167. final y = size.height * logicPoint.y;
  168. return DPoint(x, y);
  169. }
  170. // 获取点 p 在 Rect r 中的归一化坐标
  171. DPoint getZoomedPoint(Rect r, DPoint p) {
  172. double xPctInR = (p.x - r.left) / r.width;
  173. double yPctInR = (p.y - r.top) / r.height;
  174. return DPoint(xPctInR, yPctInR);
  175. }
  176. @override
  177. void setZoomRect(Rect? rect) {
  178. zoomRect = rect;
  179. }
  180. @override
  181. void paintPerfusion(
  182. Canvas canvas,
  183. Size size,
  184. ) {}
  185. /// 画序号
  186. ///
  187. /// [text] 自定义序号内容
  188. @protected
  189. void drawId(Canvas canvas, Size size, [String? text]) {
  190. final String displayText;
  191. if (refItem.parent == null) {
  192. displayText = text ?? id.toString();
  193. } else {
  194. displayText = '$id.${refItem.description}';
  195. }
  196. final point = innerPoints[0];
  197. var offset = convert2ViewPoint(size, point).toOffset();
  198. return drawCustomId(canvas, size, offset, displayText);
  199. }
  200. /// 画自定义位置的序号
  201. /// [offset] 位置
  202. /// [text] 自定义序号内容
  203. @protected
  204. void drawCustomId(Canvas canvas, Size size, Offset offset, [String? text]) {
  205. final displayText = text ?? id.toString();
  206. final fontSize = annotationFontSize * refItem.scaleRatio;
  207. final fontOffsetY = 4.0 * refItem.scaleRatio;
  208. final letterSpacing = 0.0 * refItem.scaleRatio;
  209. final style = TextStyle(
  210. fontSize: fontSize,
  211. color: MeasureColors.Primary,
  212. // fontWeight: FontWeight.bold,
  213. letterSpacing: letterSpacing,
  214. );
  215. final fontPlace = boundingTextSize(displayText, style);
  216. double transY = 0;
  217. double transX = 0;
  218. final vertexOffsetW = vertexSize / 2;
  219. if (offset.dx < fontPlace.width) {
  220. transX = fontSize + vertexOffsetW;
  221. } else {
  222. transX = -fontPlace.width - vertexOffsetW;
  223. }
  224. if (offset.dy < fontPlace.height + fontOffsetY) {
  225. transY = fontOffsetY;
  226. } else {
  227. transY = -fontPlace.height - fontOffsetY;
  228. }
  229. offset = offset.translate(transX, transY);
  230. canvas.drawText(
  231. displayText,
  232. offset,
  233. style: style,
  234. );
  235. }
  236. /// 画顶点
  237. void drawVertex(Canvas canvas, Offset offset, [bool active = false]) {
  238. canvas.drawVertex(offset, vertexSize, active: active);
  239. }
  240. /// 画小顶点(1/2 大小)由 measureData.measureSystemSetting.minCursorDistance 来控制是否触发
  241. void drawMiniVertex(Canvas canvas, Offset offset, [bool active = false]) {
  242. canvas.drawVertex(offset, vertexSize / 2, active: active);
  243. }
  244. /// 画顶点
  245. void drawCrossVertex(Canvas canvas, Offset offset, [bool active = false]) {
  246. canvas.drawCrossVertex(offset, vertexSize, active: active);
  247. }
  248. /// 画短横标记
  249. void drawMark(Canvas canvas, Offset offset,
  250. [bool active = false, bool ifHorizontal = true]) {
  251. canvas.drawMark(offset, vertexSize,
  252. active: active, ifHorizontal: ifHorizontal);
  253. }
  254. /// 计算文本长度
  255. static Size boundingTextSize(
  256. String text,
  257. TextStyle style, {
  258. int maxLines = 2 ^ 31,
  259. double maxWidth = double.infinity,
  260. }) {
  261. // if (Get.context == null) return Size.zero;
  262. if (text.isEmpty) return Size.zero;
  263. final TextPainter textPainter = TextPainter(
  264. textDirection: TextDirection.ltr,
  265. // locale: Localizations.localeOf(Get.context!),
  266. text: TextSpan(text: text, style: style),
  267. maxLines: maxLines,
  268. )..layout(maxWidth: maxWidth);
  269. return textPainter.size;
  270. }
  271. void _recordHistory() {
  272. if (refItem.parent == null) {
  273. final recorder = refItem.application.recorder;
  274. recorder.recordMeasureItem(refItem.meta.name);
  275. _measureModeName = refItem.application.currentMode.modeType.name;
  276. }
  277. }
  278. @override
  279. bool checkCanPaint() {
  280. if (refItem is ITopMeasureItem) {
  281. return true;
  282. }
  283. bool result = true;
  284. final app = refItem.application;
  285. if (app.crossFrameContext != null && !app.crossFrameContext!.isOver) {
  286. final imgSign = app.crossFrameContext!.currentUrl.hashCode;
  287. final frameIndex = app.frameData!.index;
  288. result = result && checkFeatureCanPaintInCrossFrame(imgSign, frameIndex);
  289. }
  290. return result;
  291. }
  292. /// 判断跨帧状态中Future是否可绘制
  293. bool checkFeatureCanPaintInCrossFrame(
  294. int imgSign,
  295. int index,
  296. ) {
  297. if (imageBelongSign != null && imageBelongSign != imgSign) {
  298. return false;
  299. }
  300. if (frameIndex != null && frameIndex != index) {
  301. return false;
  302. }
  303. return true;
  304. }
  305. }