944 lines
31 KiB
C#
Raw Normal View History

2025-03-16 13:11:08 +08:00

using DH.Commons.Enums;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using static DH.Commons.Enums.EnumHelper;
namespace DH.UI.Model.Winform
{
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;
// EventRouter.ChangeElementsMouseState -= OnElementChangeMouseState;
// EventRouter.ChangeElementsMouseState += OnElementChangeMouseState;
//Elements.CollectionChanged += Elements_CollectionChanged;
}
private void Elements_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//this.Invoke(new Action(() =>
//{
// this.Invalidate();
//}));
}
#region Event
public Action<MouseState> OnMouseStateChanged;
public Action<IShapeElement> DrawTemplateChanged = null;
public Action<Point, PointF, string> OnMouseLocationUpdated;
#endregion
private MouseState mouseState = MouseState.Normal;
public MouseState MouseState
{
get
{
return mouseState;
}
set
{
if (mouseState != value)
{
mouseState = value;
// OnMouseStateChanged?.BeginInvoke(value, null, null);
Task.Run(() => OnMouseStateChanged.Invoke(value));
}
}
}
#region
//private Bitmap map = new Bitmap(10, 10);
//public Bitmap MAP
//{
// get
// {
// _mapLoadHandler.WaitOne();
// return map;
// }
// set
// {
// map = value;
// }
//}
public Bitmap MAP { get; set; } = new Bitmap(10, 10);
public Matrix Matrix { get; set; } = new Matrix();
public ObservableCollection<IShapeElement> Elements { get; set; } = new ObservableCollection<IShapeElement>();
RectangleF _selectionRect = new RectangleF();
#endregion
#region
protected override void OnPaint(PaintEventArgs e)
{
try
{
//lock (_mapLoadLock)
//{ }
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.InterpolationMode = InterpolationMode.NearestNeighbor;
g.Clear(BackColor);
g.MultiplyTransform(Matrix);
if (MAP != null)
{
try
{
g.DrawImage(MAP, 0, 0, MAP.Width, MAP.Height);
}
catch (Exception ex)
{
}
}
else
{
g.Clear(BackColor);
}
DrawTemplate?.Draw(g);
foreach (IShapeElement ele in Elements)
{
if (ele.IsEnabled && ele.IsShowing)
{
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.X, _selectionRect.Y, _selectionRect.Width, _selectionRect.Height);
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 IShapeElement drawTemplate = null;
public IShapeElement DrawTemplate
{
get
{
return drawTemplate;
}
set
{
if (drawTemplate != value)
{
drawTemplate = value;
//DrawTemplateChanged?.BeginInvoke(value, null, null);
Task.Run(() => DrawTemplateChanged.Invoke(value));
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(IShapeElement ele)
{
//int maxIndex = 1;
//if (Elements.Count > 0)
//{
// maxIndex = Elements.Max(u => u.Index) + 1;
//}
//ele.Index = maxIndex;
//ele.Name = maxIndex.ToString();
//#region 获取基元的设备属性,目前包括运动坐标和相机参数
//SetElementDevicePara?.Invoke(ele);
//#endregion
//Elements.Add(ele);
//DrawTemplate = DrawTemplate?.Clone() as IShapeElement;
}
#endregion
#region
private void OnElementChangeMouseState(IShapeElement ele, ElementState preState, ElementState curState)
{
if (curState != ElementState.Normal)
{
switch (curState)
{
case ElementState.New:
MouseState = MouseState.New;
break;
case ElementState.Selected:
CurrentElementId = ele.ID;
Cursor = Cursors.Default;
break;
case ElementState.Moving:
MouseState = MouseState.MoveElement;
Cursor = Cursors.NoMove2D;
break;
case ElementState.Editing:
MouseState = MouseState.Editing;
Cursor = Cursors.Hand;
break;
case ElementState.MouseInSide:
MouseState = MouseState.InSideElement;
break;
case ElementState.MouseHover:
MouseState = MouseState.HoverElement;
break;
case ElementState.CanStretchLeft:
Cursor = Cursors.SizeWE;
break;
case ElementState.StretchingLeft:
MouseState = MouseState.StretchingLeft;
Cursor = Cursors.SizeWE;
break;
case ElementState.CanStretchBottom:
Cursor = Cursors.SizeNS;
break;
case ElementState.StretchingBottom:
MouseState = MouseState.StretchingBottom;
Cursor = Cursors.SizeNS;
break;
case ElementState.CanStretchRight:
Cursor = Cursors.SizeWE;
break;
case ElementState.StretchingRight:
MouseState = MouseState.StretchingRight;
Cursor = Cursors.SizeWE;
break;
case ElementState.CanStretchTop:
Cursor = Cursors.SizeNS;
break;
case ElementState.StretchingTop:
MouseState = MouseState.StretchingTop;
Cursor = Cursors.SizeNS;
break;
case ElementState.CanStretchLeftLowerCorner:
Cursor = Cursors.SizeNESW;
break;
case ElementState.StretchingLeftLowerCorner:
MouseState = MouseState.StretchingLeftLowerCorner;
Cursor = Cursors.SizeNESW;
break;
case ElementState.CanStretchLeftUpperCorner:
Cursor = Cursors.SizeNWSE;
break;
case ElementState.StretchingLeftUpperCorner:
MouseState = MouseState.StretchingLeftUpperCorner;
Cursor = Cursors.SizeNWSE;
break;
case ElementState.CanStretchRightLowerCorner:
Cursor = Cursors.SizeNWSE;
break;
case ElementState.StretchingRightLowerCorner:
MouseState = MouseState.StretchingRightLowerCorner;
Cursor = Cursors.SizeNWSE;
break;
case ElementState.CanStretchRightUpperCorner:
Cursor = Cursors.SizeNESW;
break;
case ElementState.StretchingRightUpperCorner:
MouseState = MouseState.StretchingRightUpperCorner;
Cursor = Cursors.SizeNESW;
break;
default:
//MouseState = MouseState.Normal;
break;
}
}
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;
}
}
this.Invalidate();
}
#endregion
#region
private void Canvas_MouseWheel(object sender, MouseEventArgs e)
{
PointF 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);
}
PointF afterPoint = ToMapPoint(e.Location);
//后平移
Matrix.Translate(afterPoint.X - prePoint.X, afterPoint.Y - prePoint.Y);
Invalidate();
}
PointF startPoint, currentPoint;
bool _isMouseBtnPressing = false;
private void Canvas_MouseDown(object sender, MouseEventArgs e)
{
PointF p = ToMapPoint(e.Location);
if (e.Button == MouseButtons.Left)
{
_isMouseBtnPressing = true;
switch (MouseState)
{
case MouseState.Normal:
startPoint = e.Location;
break;
case MouseState.StretchingLeft:
break;
case MouseState.StretchingRight:
break;
case MouseState.StretchingTop:
break;
case MouseState.StretchingBottom:
break;
case MouseState.MoveElement:
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;
}
foreach (IShapeElement ele in Elements)
{
ele.OnMouseDown(p);
}
}
else if (e.Button == MouseButtons.Right)
{
if (DrawTemplate != null && DrawTemplate.State == ElementState.New && DrawTemplate.IsCreatedDone())
{
IShapeElement ele = DrawTemplate.Clone() as IShapeElement;
ele.State = ElementState.Normal;
Elements.Add(ele);
// (DrawTemplate as ElementBase).Initial();
}
}
}
private void Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
_isMouseBtnPressing = false;
switch (MouseState)
{
case MouseState.Normal:
break;
case MouseState.HoverElement:
break;
case MouseState.InSideElement:
break;
case MouseState.StretchingLeft:
break;
case MouseState.StretchingRight:
break;
case MouseState.StretchingTop:
break;
case MouseState.StretchingBottom:
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;
foreach (IShapeElement ele in Elements)
{
ele.State = ElementState.Normal;
if (ele.IsIntersect(_selectionRect))
{
ele.State = ElementState.Selected;
}
}
break;
}
Cursor = Cursors.Default;
if (MouseState != MouseState.SelectionZone)
{
PointF p = ToMapPoint(e.Location);
DrawTemplate?.OnMouseUp(p);
foreach (IShapeElement ele in Elements)
{
ele.OnMouseUp(p);
}
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
PointF p = ToMapPoint(e.Location);
switch (MouseState)
{
case MouseState.Normal:
{
if (_isMouseBtnPressing)
{
currentPoint = e.Location;
PointF p1 = ToMapPoint(startPoint);
PointF p2 = ToMapPoint(currentPoint);
Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y);
startPoint = e.Location;
}
}
break;
case MouseState.StretchingLeft:
break;
case MouseState.StretchingRight:
break;
case MouseState.StretchingTop:
break;
case MouseState.StretchingBottom:
break;
case MouseState.MoveElement:
break;
case MouseState.HoverElement:
case MouseState.InSideElement:
case MouseState.New:
DrawTemplate?.OnMouseMove(p);
break;
case MouseState.Editing:
break;
case MouseState.MovingAll:
break;
case MouseState.SelectionZoneDoing:
{
currentPoint = p;
float[] x2 = new float[2] { startPoint.X, currentPoint.X };
float[] y2 = new float[2] { startPoint.Y, currentPoint.Y };
float xMin = x2.Min();
float xMax = x2.Max();
float yMin = y2.Min();
float yMax = y2.Max();
_selectionRect = new RectangleF(xMin, yMin, xMax - xMin, yMax - yMin);
}
break;
}
PointF mapPoint = ToMapPoint(e.Location);
Color color = Color.Transparent;
if (MAP != null && mapPoint.X > 0 && mapPoint.X < MAP.Width && mapPoint.Y > 0 && mapPoint.Y < MAP.Height)
{
color = MAP.GetPixel((int)mapPoint.X, (int)mapPoint.Y);
}
// OnMouseLocationUpdated?.BeginInvoke(e.Location, mapPoint, color.Name, null, null);
Task.Run(() => OnMouseLocationUpdated?.Invoke(e.Location, mapPoint, color.Name));
if (MouseState != MouseState.SelectionZoneDoing)
{
Elements.ToList().ForEach(ele => ele?.OnMouseMove(p));
}
Invalidate();
}
private void Canvas_MouseDoubleClick(object sender, MouseEventArgs e)
{
PointF 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 (IShapeElement ele in Elements)
{
ele.OnMouseDoubleClick(p);
}
//);
break;
default:
break;
}
}
else
{
//if (MouseState == MouseState.SelectedElement)
{
MouseState = MouseState.Normal;
//Elements.ForEach(ele =>
foreach (IShapeElement ele in Elements)
{
ele.State = ElementState.Normal;
}
//);
}
}
}
#endregion
#region
bool _firstLoad = true;
object _mapLoadLock = new object();
ManualResetEvent _mapLoadHandler = new ManualResetEvent(true);
ManualResetEvent _mapUsingHandler = new ManualResetEvent(false);
/// <summary>
/// 载入图片
/// </summary>
/// <param name="bitmap"></param>
public void LoadImage(Bitmap bitmap)
{
if (bitmap == null)
return;
////lock (_mapLoadLock)
////_mapUsingHandler.WaitOne();
//_mapLoadHandler.Reset();
//{
// map?.Dispose();
// map = null;
// map = bitmap;
//}
//_mapLoadHandler.Set();
MAP = bitmap;
if (_firstLoad)
{
SetScreenSize();
_firstLoad = false;
}
Invalidate();
}
public void Clear()
{
MAP = null;
Elements.Clear();
Invalidate();
}
/// <summary>
/// 设置图片为原始尺寸
/// </summary>
public void SetMapSize()
{
Matrix = new Matrix();
Invalidate();
}
/// <summary>
/// 设置图片为适配尺寸
/// </summary>
public void SetScreenSize()
{
try
{
if (MAP == null)
return;
Matrix = new Matrix();
//先缩放
List<float> ratios = new List<float>() { MAP.Width / (float)Width, MAP.Height / (float)Height };
float ratio = 1 / ratios.Max();
Matrix.Scale(ratio, ratio);
//再平移
//将plMain的中心转换为图片坐标
PointF screenCenter = new PointF(Width / 2.0f, Height / 2.0f);
PointF mapPoint = ToMapPoint(screenCenter);
//目标坐标减去当前坐标
Matrix.Translate(-MAP.Width / 2.0f + mapPoint.X, -MAP.Height / 2.0f + 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 PointF ToMapPoint(PointF p)
{
PointF[] ps = new PointF[] { 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 (IShapeElement ele in Elements)
// {
// ele.State = ElementState.Normal;
// }
// //);
// }
//}
//Invalidate();
}
public void OnCanvasKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete) //delete键
{
Elements.Remove(Elements.FirstOrDefault(u => u.ID == CurrentElementId));
}
if (e.KeyData == Keys.Escape) //Esc
{
if (DrawTemplate != null /*&& (DrawTemplate as ElementBase).CreatePoints.Count > 0*/)
{
DrawTemplate.Initial();
}
else
{
DrawTemplate = null;
if (MouseState != MouseState.Normal)
{
MouseState = MouseState.Normal;
}
else
{
Elements.ToList().ForEach(u => u.State = ElementState.Normal);
}
}
}
//if (e.KeyData == Keys.Up)
//{
// Elements.ToList().ForEach(u =>
// {
// if (u.State == ElementState.Selected)
// {
// u.Translate(0, -1);
// }
// });
//}
//if (e.KeyData == Keys.Down)
//{
// Elements.ToList().ForEach(u =>
// {
// if (u.State == ElementState.Selected)
// {
// u.Translate(0, 1);
// }
// });
//}
//if (e.KeyData == Keys.Left)
//{
// Elements.ToList().ForEach(u =>
// {
// if (u.State == ElementState.Selected)
// {
// u.Translate(-1, 0);
// }
// });
//}
//if (e.KeyData == Keys.Right)
//{
// Elements.ToList().ForEach(u =>
// {
// if (u.State == ElementState.Selected)
// {
// u.Translate(1, 0);
// }
// });
//}
Invalidate();
}
#endregion
#region
public Action<IShapeElement> SetElementDevicePara;
public Action<IShapeElement> 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
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
MAP?.Dispose();
Matrix?.Dispose();
penGrid?.Dispose();
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
}
}