item_feature.dart 9.5 KB

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