item_feature.dart 9.6 KB

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