using Bro.Common.Helper;
|
using Bro.Common.Interface;
|
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.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;
|
}
|
|
#region Event
|
public Action<MouseState> OnMouseStateChanged;
|
public Action<IShapeElement> DrawTemplateChanged = null;
|
public Action<Point, Point> 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(10, 10);
|
public Matrix Matrix { get; set; } = new Matrix();
|
public ObservableCollection<IShapeElement> Elements { get; set; } = new ObservableCollection<IShapeElement>();
|
|
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 (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);
|
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);
|
|
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:
|
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 (IShapeElement 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 (IShapeElement 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 (IShapeElement 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 (IShapeElement 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 (IShapeElement 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 (IShapeElement 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 (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;
|
|
/// <summary>
|
/// 载入图片
|
/// </summary>
|
/// <param name="map"></param>
|
public void LoadImage(Bitmap map)
|
{
|
if (map == null)
|
return;
|
|
MAP = map;
|
//MAP = map;
|
|
if (_firstLoad)
|
{
|
SetScreenSize();
|
_firstLoad = false;
|
}
|
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的中心转换为图片坐标
|
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 (IShapeElement 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 (IShapeElement ele in Elements)
|
{
|
ele.State = ElementState.Normal;
|
}
|
//);
|
}
|
break;
|
}
|
|
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
|
}
|
}
|