MarkManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. using System;
  2. using System.Collections.Generic;
  3. //using System.Drawing;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Media;
  10. using System.Windows.Shapes;
  11. namespace MeterVision.Mark
  12. {
  13. //水表图片标注管理
  14. public class MarkManager
  15. {
  16. private readonly Brush[] _brushes = new Brush[] { Brushes.Red, Brushes.Orange, Brushes.Yellow, Brushes.Green };
  17. //画布
  18. private Canvas _canvas;
  19. //最多4个点
  20. private List<Point> _points;
  21. //用来保存已绘制的图像
  22. private List<UIElement> _drawShapes;
  23. //要求的画点数(只有2、3、4三种情况)
  24. //1. 2个点取指针表第一个指针与最后一个指针的直线
  25. //2. 3个点取数字区域的选取
  26. //3. 4个点表盘区域的选取
  27. public int PointCount { get; private set; }
  28. //public int _curPointCount = 0;
  29. public bool IsMarking { get; private set; } //正在标注标记
  30. //标注完成事件
  31. public event Action<int, List<Point>, int> FinishMark;
  32. public event Action<string> MarkError;
  33. //记录临时直线图像
  34. private Point lastFixedPoint; //上一个按下的点
  35. private Line tempLine; //临时线
  36. private bool isFirstClick = true;
  37. public MarkManager(Canvas canvas)
  38. {
  39. _canvas = canvas;
  40. _points = new List<Point>();
  41. _drawShapes = new List<UIElement>();
  42. PointCount = 0;
  43. IsMarking = false;
  44. }
  45. //开始标注
  46. public void ReadyMark(int pointCount)
  47. {
  48. //删除所有绘制的形状,不能删除表盘图像
  49. foreach (var shape in _drawShapes)
  50. {
  51. _canvas.Children.Remove(shape);
  52. }
  53. _drawShapes.Clear();
  54. _points.Clear();
  55. PointCount = pointCount;
  56. IsMarking = true;
  57. //_curPointCount = 0;
  58. InitDrawTempLine(pointCount);
  59. }
  60. //删除临时线
  61. private void InitDrawTempLine(int pointCount)
  62. {
  63. isFirstClick = pointCount == 3 ? true : false;
  64. }
  65. //清除标注
  66. public void ClearMark()
  67. {
  68. ReadyMark(PointCount);
  69. //IsMarking = false;
  70. }
  71. public void InitMark()
  72. {
  73. ReadyMark(0);
  74. IsMarking = false;
  75. }
  76. public void StopMark()
  77. {
  78. IsMarking = false;
  79. }
  80. private void AddPoint(Point point)
  81. {
  82. int index = _points.Count;
  83. // 绘制点击点
  84. Ellipse pointEllipse = new Ellipse
  85. {
  86. Width = 5,
  87. Height = 5,
  88. Fill = _brushes[index]
  89. };
  90. Canvas.SetLeft(pointEllipse, point.X - 2.5); // 调整点的中心位置
  91. Canvas.SetTop(pointEllipse, point.Y - 2.5);
  92. _canvas.Children.Add(pointEllipse);
  93. _drawShapes.Add(pointEllipse);
  94. _points.Add(point);
  95. }
  96. //更新指定位置的点坐标及标注
  97. private void UpdatePoint(Point point, int position)
  98. {
  99. int index = _canvas.Children.IndexOf(_drawShapes[position - 1]);
  100. _canvas.Children.RemoveAt(index);
  101. Ellipse pointEllipse = new Ellipse
  102. {
  103. Width = 5,
  104. Height = 5,
  105. Fill = _brushes[position - 1]
  106. };
  107. Canvas.SetLeft(pointEllipse, point.X - 2.5); // 调整点的中心位置
  108. Canvas.SetTop(pointEllipse, point.Y - 2.5);
  109. _canvas.Children.Insert(index, pointEllipse);
  110. _drawShapes.RemoveAt(position - 1);
  111. _drawShapes.Insert(position - 1, pointEllipse);
  112. _points.RemoveAt(position - 1);
  113. _points.Insert(position - 1, point);
  114. }
  115. //点标注
  116. public void PointMark(Point point)
  117. {
  118. if (IsMarking && _points.Count < PointCount)
  119. {
  120. AddPoint(point);
  121. if (_points.Count == PointCount)
  122. {
  123. if (PointCount == 4)
  124. {
  125. //四点标表盘
  126. FourPoint_DrawRectangle();
  127. }
  128. else if (PointCount == 3)
  129. {
  130. //三点画矩形
  131. //删除画布中的直线元素(因为是临时的)
  132. for(int i= _canvas.Children.Count-1; i>=0;i--)
  133. {
  134. UIElement element = _canvas.Children[i];
  135. if (element is Line)
  136. {
  137. _canvas.Children.Remove(element);
  138. }
  139. }
  140. ThreePoint_DrawRectangle();
  141. }
  142. else if (PointCount == 2)
  143. {
  144. TwoPoint_DrawLine();
  145. }
  146. }//if _points.Count == PointCount
  147. else if(PointCount == 3 && _points.Count < PointCount)
  148. {
  149. if (isFirstClick)
  150. {
  151. //第一次电子,记录起点
  152. lastFixedPoint = point;
  153. isFirstClick = false;
  154. }
  155. else
  156. {
  157. //固定上一条线
  158. Line fixedLine = new Line
  159. {
  160. X1 = lastFixedPoint.X,
  161. Y1 = lastFixedPoint.Y,
  162. X2 = point.X,
  163. Y2 = point.Y,
  164. Stroke = Brushes.Red,
  165. StrokeThickness = 2
  166. };
  167. _canvas.Children.Add(fixedLine);
  168. //更新固定点
  169. lastFixedPoint = point;
  170. }
  171. //创建新的临时线(鼠标实时跟随)
  172. tempLine = new Line
  173. {
  174. X1 = lastFixedPoint.X,
  175. Y1 = lastFixedPoint.Y,
  176. X2 = lastFixedPoint.X,
  177. Y2 = lastFixedPoint.Y,
  178. Stroke = Brushes.Red,
  179. StrokeThickness = 2
  180. };
  181. _canvas.Children.Add(tempLine);
  182. }//else if
  183. }//if IsMarking
  184. }
  185. //画临时线
  186. public void MouseDrawLine(Point point)
  187. {
  188. //第一个点,第二个点后,要画临时线
  189. if(IsMarking && PointCount == 3 && _points.Count >0 && _points.Count <= 2 )
  190. {
  191. if (tempLine != null)
  192. {
  193. //Point currentPoint = e.GetPosition(MyCanvas);
  194. tempLine.X2 = point.X;
  195. tempLine.Y2 = point.Y;
  196. }
  197. }
  198. }
  199. // 判断三点是否共线
  200. private bool ArePointsCollinear(Point p1, Point p2, Point p3)
  201. {
  202. // 使用叉积判断三点是否共线
  203. double crossProduct = (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X);
  204. return Math.Abs(crossProduct) < 0.0001;
  205. }
  206. //计算L12与L23的交点,作为新的第二个点
  207. private Point CalculatePerpendicularIntersection(Point p1, Point p2, Point p3)
  208. {
  209. double x1 = p1.X, y1 = p1.Y;
  210. double x2 = p2.X, y2 = p2.Y;
  211. double x3 = p3.X, y3 = p3.Y;
  212. // 计算L12的斜率
  213. double slopeL23 = (y3 - y2) / (x3 - x2);
  214. // 计算L23的斜率,L23垂直于L12,所以斜率为 -1 / slopeL12
  215. double slopeL12 = -1 / slopeL23;
  216. // L12的方程为:y = slopeL12 * x + b1
  217. double b1 = y1 - slopeL12 * x1;
  218. // L23的方程为:y = slopeL23 * x + b2
  219. double b2 = y3 - slopeL23 * x3;
  220. // 计算L12与L23的交点
  221. double intersectX = (b2 - b1) / (slopeL12 - slopeL23);
  222. double intersectY = slopeL12 * intersectX + b1;
  223. return new Point(intersectX, intersectY);
  224. }
  225. //计算矩形的第四个点
  226. private Point CalculateFourthPoint(Point p1, Point p2, Point p3)
  227. {
  228. // 计算从P1到P2的向量
  229. double dx1 = p2.X - p1.X;
  230. double dy1 = p2.Y - p1.Y;
  231. // 计算从P1到P3的向量
  232. double dx2 = p3.X - p1.X;
  233. double dy2 = p3.Y - p1.Y;
  234. // 通过平行四边形法则计算第四个点
  235. double x4 = p3.X - dx1;
  236. double y4 = p3.Y - dy1;
  237. return new Point(x4, y4);
  238. }
  239. // 绘制旋转矩形,连接四个顶点
  240. private void DrawRotatedRectangle(Point p1, Point p2, Point p3, Point p4)
  241. {
  242. // 绘制矩形的四条边
  243. Line line1 = new Line
  244. {
  245. X1 = p1.X,
  246. Y1 = p1.Y,
  247. X2 = p2.X,
  248. Y2 = p2.Y,
  249. Stroke = Brushes.Blue,
  250. StrokeThickness = 2
  251. };
  252. Line line2 = new Line
  253. {
  254. X1 = p2.X,
  255. Y1 = p2.Y,
  256. X2 = p3.X,
  257. Y2 = p3.Y,
  258. Stroke = Brushes.Blue,
  259. StrokeThickness = 2
  260. };
  261. Line line3 = new Line
  262. {
  263. X1 = p3.X,
  264. Y1 = p3.Y,
  265. X2 = p4.X,
  266. Y2 = p4.Y,
  267. Stroke = Brushes.Blue,
  268. StrokeThickness = 2
  269. };
  270. Line line4 = new Line
  271. {
  272. X1 = p4.X,
  273. Y1 = p4.Y,
  274. X2 = p1.X,
  275. Y2 = p1.Y,
  276. Stroke = Brushes.Blue,
  277. StrokeThickness = 2
  278. };
  279. _canvas.Children.Add(line1);
  280. _canvas.Children.Add(line2);
  281. _canvas.Children.Add(line3);
  282. _canvas.Children.Add(line4);
  283. _drawShapes.Add(line1);
  284. _drawShapes.Add(line2);
  285. _drawShapes.Add(line3);
  286. _drawShapes.Add(line4);
  287. }
  288. //三点画矩形
  289. private void ThreePoint_DrawRectangle()
  290. {
  291. //判断是否为三点共线
  292. if (ArePointsCollinear(_points[0], _points[1], _points[2]))
  293. {
  294. ReadyMark(PointCount);
  295. MarkError?.Invoke("三个点共线,无效的标注");
  296. return;
  297. }
  298. //计算新的第二个点
  299. Point newSecondPoint = CalculatePerpendicularIntersection(_points[0], _points[1], _points[2]);
  300. UpdatePoint(newSecondPoint, 2); //第2个位置更新
  301. //AddPoint(newSecondPoint);
  302. // 计算第四个点(矩形的另一个角)
  303. Point fourthPoint = CalculateFourthPoint(_points[0], newSecondPoint, _points[2]);
  304. AddPoint(fourthPoint); //增加第四个位置
  305. DrawRotatedRectangle(_points[0], newSecondPoint, _points[2], fourthPoint);
  306. IsMarking = false;
  307. FinishMark?.Invoke(3, _points, 0);
  308. }
  309. //四点标记表盘
  310. private void FourPoint_DrawRectangle()
  311. {
  312. //通过标记的四个点来计算矩形的坐标
  313. double xMin = Math.Min(Math.Min(_points[0].X, _points[1].X), Math.Min(_points[2].X, _points[3].X));
  314. double yMin = Math.Min(Math.Min(_points[0].Y, _points[1].Y), Math.Min(_points[2].Y, _points[3].Y));
  315. double xMax = Math.Max(Math.Max(_points[0].X, _points[1].X), Math.Max(_points[2].X, _points[3].X));
  316. double yMax = Math.Max(Math.Max(_points[0].Y, _points[1].Y), Math.Max(_points[2].Y, _points[3].Y));
  317. double width = Math.Abs(xMax - xMin);
  318. double height = Math.Abs(yMax - yMin);
  319. //画矩形
  320. var rectangle = new Rectangle
  321. {
  322. Stroke = Brushes.Blue,
  323. StrokeThickness = 2,
  324. Fill = Brushes.Transparent,
  325. Width = width,
  326. Height = height
  327. };
  328. _canvas.Children.Add(rectangle);
  329. Canvas.SetLeft(rectangle, xMin);
  330. Canvas.SetTop(rectangle, yMin);
  331. _drawShapes.Add(rectangle);
  332. List<Point> points = new List<Point>
  333. {
  334. new Point(xMin,yMin),new Point(xMax,yMax)
  335. };
  336. IsMarking = false;
  337. FinishMark?.Invoke(2, points, 0);
  338. }
  339. //2点标记直线
  340. private void TwoPoint_DrawLine()
  341. {
  342. Line line = new Line
  343. {
  344. X1 = _points[0].X,
  345. Y1 = _points[0].Y,
  346. X2 = _points[1].X,
  347. Y2 = _points[1].Y,
  348. Stroke = Brushes.Blue,
  349. StrokeThickness = 2
  350. };
  351. _canvas.Children.Add(line);
  352. _drawShapes.Add(line);
  353. IsMarking = false;
  354. FinishMark?.Invoke(3, _points, 0);
  355. }
  356. //-------------------------------------------------------------------------
  357. }
  358. }