2025-03-18 14:20:11 +08:00

944 lines
31 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}