using Bro.Common.Helper; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Windows.Forms; using static Bro.Common.Helper.EnumHelper; namespace Bro.Common.UI { public partial class CanvasImage : UserControl { public CanvasImage() { InitializeComponent(); DoubleBuffered = true; SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true); MouseWheel += Canvas_MouseWheel; KeyDown += OnCanvasKeyDown; KeyPress += OnCanvasKeyPressed; MouseDoubleClick += Canvas_MouseDoubleClick; MouseDown += Canvas_MouseDown; MouseMove += Canvas_MouseMove; MouseUp += Canvas_MouseUp; } #region Event public Action OnMouseStateChanged; public Action DrawTemplateChanged = null; public Action OnMouseLocationUpdated; #endregion private MouseState mouseState = MouseState.Normal; public MouseState MouseState { get { return mouseState; } set { if (mouseState != value) { mouseState = value; OnMouseStateChanged?.BeginInvoke(value, null, null); } } } #region 属性和字段 public Bitmap MAP { get; set; } = new Bitmap(10000, 10000); public Matrix Matrix { get; set; } = new Matrix(); public ObservableCollection Elements { get; set; } = new ObservableCollection(); Rectangle _selectionRect = new Rectangle(); #endregion #region 重绘 protected override void OnPaint(PaintEventArgs e) { try { Rectangle rect = ClientRectangle; Graphics originG = e.Graphics; BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current; BufferedGraphics myBuffer = currentContext.Allocate(originG, rect); Graphics g = myBuffer.Graphics; g.SmoothingMode = SmoothingMode.HighSpeed; g.PixelOffsetMode = PixelOffsetMode.HighSpeed; g.Clear(BackColor); g.MultiplyTransform(Matrix); if (MAP != null) { try { g.DrawImage(MAP, 0, 0, MAP.Width, MAP.Height); } catch (Exception ex) { } } DrawTemplate?.Draw(g); foreach (ElementBase ele in Elements) { if (ele.IsEnabled && ele.ShowImage) { ele.Draw(g); } } #region Grid if (MAP != null) { if (ShowGrid) { int baseX = MAP.Width / 2; int baseY = MAP.Height / 2; Point[] xPoint = new Point[] { new Point(0, baseY), new Point(MAP.Width, baseY) }; Point[] yPoint = new Point[] { new Point(baseX, 0), new Point(baseX, MAP.Height) }; g.DrawLine(new Pen(Pen_Grid.Color, 5.0f), xPoint[0], xPoint[1]); g.DrawLine(new Pen(Pen_Grid.Color, 5.0f), yPoint[0], yPoint[1]); if (GridValue > 0) { int stepX = MAP.Width / 2 / (GridValue * MAP.Width / 2 / _minGridStep / 10); int stepY = MAP.Height / 2 / (GridValue * MAP.Height / 2 / _minGridStep / 10); //int stepX = _minGridStep + (10 - GridValue) * (MAP.Width / 2 - _minGridStep) / 10; //int stepY = _minGridStep + (10 - GridValue) * (MAP.Height / 2 - _minGridStep) / 10; int yPositive = baseY; do { xPoint = new Point[] { new Point(0, yPositive), new Point(MAP.Width, yPositive) }; g.DrawLine(Pen_Grid, xPoint[0], xPoint[1]); yPositive -= stepY; } while (yPositive > 0); int yNegative = baseY; do { xPoint = new Point[] { new Point(0, yNegative), new Point(MAP.Width, yNegative) }; g.DrawLine(Pen_Grid, xPoint[0], xPoint[1]); yNegative += stepY; } while (yNegative < MAP.Height); int xPositive = baseX; do { yPoint = new Point[] { new Point(xPositive, 0), new Point(xPositive, MAP.Height) }; g.DrawLine(Pen_Grid, yPoint[0], yPoint[1]); xPositive -= stepX; } while (xPositive > 0); int xNegative = baseX; do { yPoint = new Point[] { new Point(xNegative, 0), new Point(xNegative, MAP.Height) }; g.DrawLine(Pen_Grid, yPoint[0], yPoint[1]); xNegative += stepX; } while (xNegative < MAP.Width); } } } #endregion if (MouseState == MouseState.SelectionZoneDoing) { g.DrawRectangle(Pens.AliceBlue, _selectionRect); g.FillRectangle(new SolidBrush(Color.FromArgb(40, 0, 0, 255)), _selectionRect); } myBuffer.Render(originG); g.Dispose(); myBuffer.Dispose();//释放资源 } catch (Exception) { } } private void halfTransparent() { } #endregion #region 绘制类型 private ElementBase drawTemplate = null; public ElementBase DrawTemplate { get { return drawTemplate; } set { if (drawTemplate != value) { drawTemplate = value; DrawTemplateChanged?.BeginInvoke(value, null, null); if (value == null) { MouseState = MouseState.Normal; return; } MouseState = MouseState.New; var existed = Elements.FirstOrDefault(e => e.ID == value.ID); if (existed != null) { Elements.Remove(existed); } if (DrawTemplate != null) { DrawTemplate.OnDrawDone += OnElementDrawDone; } } } } string currentElementId = ""; string CurrentElementId { get { return currentElementId; } set { if (currentElementId != value) { currentElementId = value; } } } private void OnElementDrawDone(ElementBase obj) { int maxIndex = 1; if (Elements.Count > 0) { maxIndex = Elements.Max(u => u.Index) + 1; } obj.Index = maxIndex; obj.Name = maxIndex.ToString(); #region 获取基元的设备属性,目前包括运动坐标和相机参数 SetElementDevicePara?.Invoke(obj); #endregion Elements.Add(obj); DrawTemplate = DrawTemplate?.Clone() as ElementBase; } #endregion #region 状态变换 private void OnElementChangeMouseState(ElementBase ele, ElementState preState, ElementState curState) { if (curState != ElementState.Normal) { switch (curState) { case ElementState.New: MouseState = MouseState.New; break; case ElementState.Selected: MouseState = MouseState.SelectedElement; break; case ElementState.Moving: MouseState = MouseState.MoveElement; break; case ElementState.MouseInSide: MouseState = MouseState.InSideElement; break; case ElementState.MouseHover: MouseState = MouseState.HoverElement; break; default: MouseState = MouseState.Normal; break; } if (MouseState == MouseState.SelectedElement) { CurrentElementId = ele.ID; } } else { if (Elements.All(e => e.State == ElementState.Normal)) { CurrentElementId = null; if (preState == ElementState.Selected) { DrawTemplate = null; } else if (DrawTemplate != null) { MouseState = MouseState.New; return; } MouseState = MouseState.Normal; } } } #endregion #region 鼠标动作 private void Canvas_MouseWheel(object sender, MouseEventArgs e) { Point prePoint = ToMapPoint(e.Location); //先缩放 if (e.Delta > 0) { Matrix.Scale((float)1.1, (float)1.1); } else { Matrix.Scale((float)0.9, (float)0.9); } Point afterPoint = ToMapPoint(e.Location); //后平移 Matrix.Translate(afterPoint.X - prePoint.X, afterPoint.Y - prePoint.Y); Invalidate(); } Point startPoint, currentPoint; private void Canvas_MouseDown(object sender, MouseEventArgs e) { Point p = ToMapPoint(e.Location); if (e.Button == MouseButtons.Left) { switch (MouseState) { case MouseState.Normal: //if (DrawTemplate == null && string.IsNullOrWhiteSpace(CurrentElementId)) //{ // this.Cursor = Cursors.Hand; // startPoint = e.Location; //} //else //{ // MouseState = MouseState.New; //} MouseState = MouseState.MovingAll; startPoint = e.Location; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.State = ElementState.Normal; ele.OnMouseDown(p); } //); break; case MouseState.StretchingLeft: break; case MouseState.StretchingRight: break; case MouseState.StretchingUp: break; case MouseState.StretchingDown: break; case MouseState.MoveElement: break; case MouseState.SelectedElement: //MouseState = MouseState.MoveElement; startPoint = e.Location; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.OnMouseDown(p); } //); break; case MouseState.HoverElement: case MouseState.InSideElement: case MouseState.New: DrawTemplate?.OnMouseDown(p); break; case MouseState.Editing: break; case MouseState.SelectionZone: MouseState = MouseState.SelectionZoneDoing; startPoint = p; break; } //if (DrawTemplate == null) //{ // this.Cursor = Cursors.Hand; // startPoint = e.Location; //} //Point p = ToMapPoint(e.Location); //Elements.ForEach(ele => //{ // ele.State = ElementState.Normal; // ele.OnMouseDown(p); //}); //DrawTemplate?.OnMouseDown(p); } } private void Canvas_MouseUp(object sender, MouseEventArgs e) { switch (MouseState) { case MouseState.Normal: break; case MouseState.HoverElement: break; case MouseState.InSideElement: break; case MouseState.StretchingLeft: break; case MouseState.StretchingRight: break; case MouseState.StretchingUp: break; case MouseState.StretchingDown: break; case MouseState.MoveElement: //MouseState = MouseState.SelectedElement; break; case MouseState.New: break; case MouseState.Editing: break; case MouseState.MovingAll: MouseState = MouseState.Normal; break; case MouseState.SelectionZone: break; case MouseState.SelectionZoneDoing: MouseState = MouseState.SelectionZone; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { if (ele.IsIntersect(_selectionRect)) { ele.State = ElementState.Selected; } } //); break; } Cursor = Cursors.Default; Point p = ToMapPoint(e.Location); DrawTemplate?.OnMouseUp(p); //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.OnMouseUp(p); } //); } private void Canvas_MouseMove(object sender, MouseEventArgs e) { Point p = ToMapPoint(e.Location); switch (MouseState) { case MouseState.Normal: //Elements.ForEach(u => //{ // u.OnMouseMove(p); //}); break; case MouseState.StretchingLeft: break; case MouseState.StretchingRight: break; case MouseState.StretchingUp: break; case MouseState.StretchingDown: break; case MouseState.MoveElement: //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.OnMouseMove(p); } //); break; case MouseState.HoverElement: case MouseState.InSideElement: case MouseState.New: DrawTemplate?.OnMouseMove(p); break; case MouseState.Editing: break; case MouseState.MovingAll: { currentPoint = e.Location; Point p1 = ToMapPoint(startPoint); Point p2 = ToMapPoint(currentPoint); Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y); startPoint = e.Location; } break; case MouseState.SelectionZoneDoing: { currentPoint = p; int[] x2 = new int[2] { startPoint.X, currentPoint.X }; int[] y2 = new int[2] { startPoint.Y, currentPoint.Y }; int xMin = x2.Min(); int xMax = x2.Max(); int yMin = y2.Min(); int yMax = y2.Max(); _selectionRect = new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin); } break; } //if (this.Cursor == Cursors.Hand) //{ // currentPoint = e.Location; // Point p1 = ToMapPoint(startPoint); // Point p2 = ToMapPoint(currentPoint); // Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y); // startPoint = e.Location; //} //DisplayMouseLocation(e.Location); OnMouseLocationUpdated?.BeginInvoke(e.Location, ToMapPoint(e.Location), null, null); if (MouseState != MouseState.SelectionZoneDoing) { //Elements.ForEach(u => foreach (ElementBase ele in Elements) { ele.OnMouseMove(p); } //); } Invalidate(); } private void Canvas_MouseDoubleClick(object sender, MouseEventArgs e) { Point p = ToMapPoint(e.Location); if (e.Button == MouseButtons.Left) { switch (MouseState) { case MouseState.SelectedElement: case MouseState.HoverElement: case MouseState.InSideElement: case MouseState.MoveElement: case MouseState.Normal: //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.OnMouseDoubleClick(p); } //); break; default: break; } } else { if (MouseState == MouseState.SelectedElement) { MouseState = MouseState.Normal; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.State = ElementState.Normal; } //); } } } #endregion #region 图片操作 bool _firstLoad = true; /// /// 载入图片 /// /// public void LoadImage(Bitmap map) { if (map == null) return; MAP = map; //MAP = map; if (_firstLoad) { SetScreenSize(); _firstLoad = false; } Invalidate(); } /// /// 设置图片为原始尺寸 /// public void SetMapSize() { Matrix = new Matrix(); Invalidate(); } /// /// 设置图片为适配尺寸 /// public void SetScreenSize() { try { if (MAP == null) return; Matrix = new Matrix(); //先缩放 List ratios = new List() { MAP.Width / (float)Width, MAP.Height / (float)Height }; float ratio = 1 / ratios.Max(); Matrix.Scale(ratio, ratio); //再平移 //将plMain的中心转换为图片坐标 Point screenCenter = new Point(Width / 2, Height / 2); Point mapPoint = ToMapPoint(screenCenter); //目标坐标减去当前坐标 Matrix.Translate(-MAP.Width / 2 + mapPoint.X, -MAP.Height / 2 + mapPoint.Y); Invalidate(); } catch (Exception ex) { Trace.TraceError(ex.GetExceptionMessage()); } } #endregion #region 私有方法 //private void DisplayMouseLocation(Point location) //{ // string screenPoint = string.Format("屏幕坐标X:{0};Y:{1}", location.X, location.Y); // Point mapPoint = ToMapPoint(location); // string mapPointStr = string.Format("图片坐标X:{0};Y:{1}", mapPoint.X, mapPoint.Y); // tsslLocation.Text = screenPoint + " " + mapPointStr; //} private Point ToMapPoint(Point p) { Point[] ps = new Point[] { p }; Matrix invertMatrix = Matrix.Clone(); invertMatrix.Invert(); invertMatrix.TransformPoints(ps); return ps[0]; } private Point ToScreenPoint(Point p) { Point[] ps = new Point[] { p }; Matrix.TransformPoints(ps); return ps[0]; } #endregion #region 按键操作 public void OnCanvasKeyPressed(object sender, KeyPressEventArgs e) { if (e.KeyChar == 27) //Esc { if (MouseState == MouseState.SelectedElement) { MouseState = MouseState.Normal; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.State = ElementState.Normal; } //); } } Invalidate(); } public void OnCanvasKeyDown(object sender, KeyEventArgs e) { if (e.KeyValue == 46) //delete键 { //Elements.RemoveAll(u => u.ID == CurrentElementId); Elements.Remove(Elements.FirstOrDefault(u => u.ID == CurrentElementId)); } Invalidate(); } public void OnCanvasKeyDown(object sender, Keys key) { switch (key) { case Keys.Delete: //Elements.RemoveAll(u => u.ID == CurrentElementId); Elements.Remove(Elements.FirstOrDefault(u => u.ID == CurrentElementId)); break; case Keys.Escape: if (MouseState == MouseState.SelectedElement) { MouseState = MouseState.Normal; //Elements.ForEach(ele => foreach (ElementBase ele in Elements) { ele.State = ElementState.Normal; } //); } break; } Invalidate(); } #endregion #region 基元的设备属性 运动设置和相机设置 public Action SetElementDevicePara; public Action SetDeviceByElement; #endregion #region Grid private bool showGrid = false; public bool ShowGrid { get => showGrid; set { showGrid = value; Invalidate(); } } private int gridValue = 0; public int GridValue { get => gridValue; set { gridValue = value; Invalidate(); } } private Pen penGrid = new Pen(Color.FromArgb(120, Color.Red), 1.0f); public Pen Pen_Grid { get => penGrid; set { penGrid = value; Invalidate(); } } readonly int _minGridStep = 10; #endregion #region Dispose /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { MAP?.Dispose(); Matrix?.Dispose(); penGrid?.Dispose(); if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #endregion } }