1238 lines
40 KiB
C#
1238 lines
40 KiB
C#
using System.ComponentModel;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Drawing.Text;
|
||
using System.Windows.Forms;
|
||
using XKRS.UI.Helper;
|
||
using System.Text.Json;
|
||
using System.Security.Cryptography.X509Certificates;
|
||
using System.Xml.Serialization;
|
||
using System.Drawing;
|
||
using Sunny.UI;
|
||
using Sunny.UI.Win32;
|
||
using OpenCvSharp;
|
||
using OpenCvSharp.Extensions;
|
||
using Point = System.Drawing.Point;
|
||
|
||
namespace XKRS.UI
|
||
{
|
||
public partial class Canvas : UserControl, INotifyPropertyChanged
|
||
{
|
||
|
||
#region 变量通知
|
||
public event PropertyChangedEventHandler? PropertyChanged;
|
||
private void NotifyPropertyChanged(string name)
|
||
{
|
||
if (PropertyChanged != null)
|
||
{
|
||
PropertyChanged(this, new PropertyChangedEventArgs(name));
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
|
||
[DisplayName("方向键移动间距")]
|
||
[Description("按下方向键时,选中的缺陷可以向对应的方向平移,该值设置按下一次方向键缺陷图片的移动距离,单位:像素")]
|
||
public float MoveStep { get; set; } = 5f;
|
||
|
||
[DisplayName("允许选中缺陷")]
|
||
[Description("鼠标单击时,是否允许选中图上绘制的缺陷,允许选中时才可以对缺陷进行操作")]
|
||
public bool AllowSelectDefect { get; set; } = true;
|
||
|
||
private Keys _keyLastPressed = Keys.None;
|
||
|
||
/// <summary>
|
||
/// 顺序查找已选中的缺陷
|
||
/// </summary>
|
||
private Defect? _selectedDefect;
|
||
|
||
public Defect SelectedDefect
|
||
{
|
||
get
|
||
{
|
||
_selectedDefect = DefectList.FirstOrDefault(d => d.Selected);
|
||
return _selectedDefect;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 整体缩放比例
|
||
/// </summary>
|
||
private float _wholeScale;
|
||
|
||
public float WholeScale
|
||
{
|
||
get
|
||
{
|
||
return _wholeScale;
|
||
}
|
||
set
|
||
{
|
||
_wholeScale = value;
|
||
WholeScaleChange();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 横向缩放比列
|
||
/// </summary>
|
||
private float _xScale;
|
||
|
||
public float XScale
|
||
{
|
||
get
|
||
{
|
||
return _xScale;
|
||
}
|
||
set
|
||
{
|
||
_xScale = value;
|
||
XScaleChange();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 竖向缩放比例
|
||
/// </summary>
|
||
private float _yScale;
|
||
|
||
public float YScale
|
||
{
|
||
get
|
||
{
|
||
return _yScale;
|
||
}
|
||
set
|
||
{
|
||
_yScale = value;
|
||
YScaleChange();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 缩放比例,该参数必须大于0
|
||
/// </summary>
|
||
private double _scale = 1.0;
|
||
|
||
public double Scale
|
||
{
|
||
get { return _scale; }
|
||
set
|
||
{
|
||
if (value <= 0.000001)
|
||
{
|
||
return;
|
||
}
|
||
_scale = value;
|
||
NotifyPropertyChanged("DScale");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 旋转角度
|
||
/// </summary>
|
||
private double _route = 0.0;
|
||
public double Route
|
||
{
|
||
get { return _route; }
|
||
set
|
||
{
|
||
_route = value;
|
||
DRouteChange();
|
||
NotifyPropertyChanged("DRoute");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 横向移动距离
|
||
/// </summary>
|
||
private float _xMove;
|
||
public float XMove
|
||
{
|
||
get { return _xMove; }
|
||
set
|
||
{
|
||
_xMove = value;
|
||
XMoveChange();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 竖向移动距离
|
||
/// </summary>
|
||
private float _yMove;
|
||
public float YMove
|
||
{
|
||
get { return _yMove; }
|
||
set
|
||
{
|
||
_yMove = value;
|
||
YMoveChange();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 边框颜色
|
||
/// </summary>
|
||
private Brush _brushColor;
|
||
public Brush BrushColor
|
||
{
|
||
get
|
||
{
|
||
return _brushColor;
|
||
}
|
||
set
|
||
{
|
||
_brushColor = value;
|
||
ColorChange();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 变换矩阵
|
||
/// </summary>
|
||
private Matrix _matrix = new Matrix();
|
||
public Matrix Matrix => _matrix.Clone();
|
||
|
||
/// <summary>
|
||
/// 矩形框
|
||
/// </summary>
|
||
private Rectangle _rcImg = new Rectangle(0, 0, 0, 0);
|
||
|
||
public int _width { get; private set; }
|
||
public int _height { get; private set; }
|
||
/// <summary>
|
||
/// 位图
|
||
/// </summary>
|
||
private Bitmap? _bitmap = null;
|
||
|
||
public Bitmap CanvasBitmap
|
||
{
|
||
get
|
||
{
|
||
return _bitmap;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预览缺陷图片的GraphicsPath
|
||
/// </summary>
|
||
private GraphicsPath? _path = null;
|
||
|
||
|
||
public Bitmap ImgData
|
||
{
|
||
get { return _bitmap; }
|
||
set
|
||
{
|
||
Init();
|
||
_bitmap = value;
|
||
if (null == _bitmap)
|
||
{
|
||
_rcImg.Width = 0;
|
||
_rcImg.Height = 0;
|
||
}
|
||
else
|
||
{
|
||
_rcImg.Width = _bitmap.Width;
|
||
_rcImg.Height = _bitmap.Height;
|
||
FitImage();
|
||
}
|
||
//强制控件使其工作区无效并且重新绘制自己以及任何子控件
|
||
Refresh();
|
||
}
|
||
}
|
||
|
||
private string strImgPath = string.Empty;
|
||
|
||
public string ImagePath
|
||
{
|
||
get { return strImgPath; }
|
||
set
|
||
{
|
||
try
|
||
{
|
||
if (null != _bitmap)
|
||
{
|
||
_bitmap.Dispose();
|
||
}
|
||
|
||
using (Stream s = File.Open(value, FileMode.Open))
|
||
{
|
||
ImgData = (Bitmap)Image.FromStream(s);
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
_bitmap = null;
|
||
strImgPath = string.Empty;
|
||
Refresh();
|
||
}
|
||
}
|
||
}
|
||
private Mat strImgMat = new Mat();
|
||
public Mat ImaMAt
|
||
{
|
||
get { return strImgMat; }
|
||
set
|
||
{
|
||
try
|
||
{
|
||
if (null != _bitmap)
|
||
{
|
||
_bitmap.Dispose();
|
||
}
|
||
|
||
|
||
ImgData = value.ToBitmap();
|
||
|
||
}
|
||
catch (Exception)
|
||
{
|
||
_bitmap = null;
|
||
strImgPath = string.Empty;
|
||
Refresh();
|
||
}
|
||
}
|
||
}
|
||
private PointF _panBasePoint = PointF.Empty;
|
||
private ClickArea _clickArea = ClickArea.AREA_UNKNOW;
|
||
public List<Defect> DefectList = new List<Defect>();
|
||
public List<GraphicsPath> GPathList = new List<GraphicsPath>();
|
||
Action<object, MouseEventArgs> actionMove;
|
||
Action<object, MouseEventArgs> actionUp;
|
||
Action<object, MouseEventArgs> actionDown;
|
||
//action1+=
|
||
object osender;
|
||
private Dictionary<MouseButtons, Action<object, MouseEventArgs>> _mouseEventDic = new Dictionary<MouseButtons, Action<object, MouseEventArgs>>()
|
||
{
|
||
|
||
|
||
};
|
||
|
||
public Canvas()
|
||
{
|
||
InitializeComponent();
|
||
|
||
DoubleBuffered = true;
|
||
SetStyle(ControlStyles.AllPaintingInWmPaint//控件忽略窗口消息 WM_ERASEBKGND 以减少闪烁
|
||
| ControlStyles.UserPaint//由控件而不是由操作系统来绘制控件自身,即自定义绘制
|
||
| ControlStyles.OptimizedDoubleBuffer//控件将首先绘制到缓冲区而不是直接绘制到屏幕,这可以减少闪烁
|
||
| ControlStyles.ResizeRedraw, true);//控件会在调整大小时进行重绘
|
||
|
||
//添加鼠标滑轮事件
|
||
//MouseWheel += OnCanvasMouseWheel;
|
||
//actionMove += ImageMoveFunc;
|
||
// actionUp += ImageUPFunc;
|
||
// actionDown += ImageDownFunc;
|
||
_mouseEventDic.Add(MouseButtons.Left, actionMove);
|
||
// _mouseEventDic.Add(MouseButtons.Left, actionMove);
|
||
// _mouseEventDic.Add(MouseButtons.Left, actionMove);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 初始化
|
||
/// </summary>
|
||
private void Init()
|
||
{
|
||
_clickArea = ClickArea.AREA_UNKNOW;//枚举
|
||
_rcImg = new Rectangle(0, 0, 0, 0);
|
||
Scale = 1.0;
|
||
Route = 0.0;
|
||
_matrix.Reset();//重置为单位矩阵
|
||
strImgPath = string.Empty;
|
||
_bitmap = null;
|
||
}
|
||
|
||
protected override void OnPaint(PaintEventArgs e)
|
||
{
|
||
//base.OnPaint(e);
|
||
try
|
||
{
|
||
//获取表示控件的工作区的矩形
|
||
Rectangle rect = ClientRectangle;
|
||
|
||
Graphics oriGraphics = e.Graphics;
|
||
|
||
//设置平滑程度为高质量,减少抗锯齿的出现
|
||
oriGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||
|
||
// 双缓冲绘图
|
||
//获取当前应用程序域的 BufferedGraphicsContext,此实例管理该应用程序的所有默认双缓冲
|
||
BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;
|
||
//BufferedGraphics 对象管理与呈现图面(例如窗体或控件)关联的内存缓冲
|
||
BufferedGraphics myBuffer = currentContext.Allocate(oriGraphics, rect);
|
||
//实例化一个直接表示内存缓冲的 Graphics 对象,可将图形呈现到内存缓冲,绘制到此对象中
|
||
Graphics bufferGraphics = myBuffer.Graphics;
|
||
bufferGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||
//高质量低速度呈现
|
||
bufferGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||
//使用的插值算法
|
||
bufferGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||
//清除整个绘图面并以指定背景色填充,Console.BackColor指获取当前控件的背景色
|
||
bufferGraphics.Clear(BackColor);
|
||
|
||
bufferGraphics.MultiplyTransform(_matrix);
|
||
|
||
|
||
#region 画背景图
|
||
|
||
if (_bitmap != null)
|
||
{
|
||
try
|
||
{
|
||
bufferGraphics.DrawImage(_bitmap, 0, 0, _bitmap.Width, _bitmap.Height);
|
||
if (_path != null)
|
||
{
|
||
bufferGraphics.DrawPath(new Pen(Brushes.Yellow, 1f), _path);
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
else
|
||
{
|
||
_bitmap = null;
|
||
return;
|
||
}
|
||
|
||
#endregion
|
||
#region 画矩形
|
||
GPathList.ForEach(path =>
|
||
{
|
||
if (path != null)
|
||
{
|
||
bufferGraphics.DrawPath(new Pen(Brushes.Green, 2f), path);
|
||
}
|
||
});
|
||
#endregion
|
||
|
||
#region 画缺陷
|
||
|
||
DefectList.ForEach(item =>
|
||
{
|
||
// 缺陷图片
|
||
Bitmap bitmapDefect = item.OriBitMap;
|
||
|
||
// 基准矩阵为背景图的矩阵
|
||
Matrix baseMatrix = _matrix.Clone();
|
||
baseMatrix.Multiply(item.PrivateMatrix); // 右乘每个缺陷自己的矩阵
|
||
bufferGraphics.Transform = baseMatrix;
|
||
|
||
PointF upperLeftPoint = item.UpperLeftPoint;
|
||
bufferGraphics.DrawImage(bitmapDefect, upperLeftPoint.X, upperLeftPoint.Y, bitmapDefect.Width, bitmapDefect.Height);
|
||
if (item.Selected)
|
||
{
|
||
// bufferGraphics.DrawPath(new Pen(Brushes.Yellow, 1f), item.Path);
|
||
item.DrawPath(bufferGraphics);
|
||
}
|
||
baseMatrix.Dispose();
|
||
});
|
||
|
||
#endregion
|
||
|
||
myBuffer.Render(oriGraphics);
|
||
//释放资源
|
||
bufferGraphics.Dispose();
|
||
myBuffer.Dispose();
|
||
currentContext.Dispose();
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 鼠标滚轮事件
|
||
/// </summary>
|
||
private void Canvas_MouseWheel(object? sender, MouseEventArgs e)
|
||
{
|
||
//绑定滚轮键
|
||
if (_clickArea == ClickArea.AREA_UNKNOW || _clickArea == ClickArea.AREA_IMG)
|
||
{
|
||
var delta = e.Delta;
|
||
var p = e.Location.ToImageCoordinate(_matrix);
|
||
var mat = new Matrix();
|
||
mat.Translate(p.X, p.Y);
|
||
if (delta > 0)
|
||
{
|
||
mat.Scale(1.1f, 1.1f);
|
||
}
|
||
else
|
||
{
|
||
mat.Scale(0.9f, 0.9f);
|
||
}
|
||
mat.Translate(-p.X, -p.Y);
|
||
_matrix.Multiply(mat);
|
||
Refresh();
|
||
}
|
||
else if (_clickArea == ClickArea.AREA_DEFECT)
|
||
{
|
||
var delta = e.Delta;
|
||
var tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
var point = e.Location.ToImageCoordinate(tmpMatrix);
|
||
SelectedDefect.Move(point.X, point.Y);
|
||
if (delta > 0)
|
||
{
|
||
SelectedDefect.Scale(1.1f, 1.1f);
|
||
SelectedDefect.WholeDScale *= 1.1f;
|
||
SelectedDefect.XDScale *= 1.1f;
|
||
SelectedDefect.YDScale *= 1.1f;
|
||
for (int i = 0; i < SelectedDefect.LastSetScale.Length; i++)
|
||
{
|
||
SelectedDefect.LastSetScale[i] *= 1.1f;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SelectedDefect.Scale(0.9f, 0.9f);
|
||
SelectedDefect.WholeDScale *= 0.9f;
|
||
SelectedDefect.XDScale *= 0.9f;
|
||
SelectedDefect.YDScale *= 0.9f;
|
||
for (int i = 0; i < SelectedDefect.LastSetScale.Length; i++)
|
||
{
|
||
SelectedDefect.LastSetScale[i] *= 0.9f;
|
||
}
|
||
}
|
||
SelectedDefect.Move(-point.X, -point.Y);
|
||
Refresh();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 整体缩放比列改变时触发
|
||
/// </summary>
|
||
private void WholeScaleChange()
|
||
{
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
float scaleWhole = WholeScale / SelectedDefect.LastSetScale[0];
|
||
SelectedDefect.LastSetScale[0] = WholeScale;
|
||
SelectedDefect.WholeDScale = WholeScale;
|
||
SelectedDefect.XDScale *= scaleWhole;
|
||
SelectedDefect.YDScale *= scaleWhole;
|
||
SelectedDefect.LastSetScale[1] *= scaleWhole;
|
||
SelectedDefect.LastSetScale[2] *= scaleWhole;
|
||
|
||
var point = SelectedDefect._defectCenterPoint;
|
||
SelectedDefect.Move(point.X, point.Y);
|
||
SelectedDefect.Scale(scaleWhole, scaleWhole);
|
||
SelectedDefect.Move(-point.X, -point.Y);
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 横向缩放比例改变时触发
|
||
/// </summary>
|
||
private void XScaleChange()
|
||
{
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
float scaleX = XScale / SelectedDefect.LastSetScale[1];
|
||
SelectedDefect.LastSetScale[1] = XScale;
|
||
SelectedDefect.XDScale = XScale;
|
||
|
||
var point = SelectedDefect._defectCenterPoint;
|
||
SelectedDefect.Move(point.X, point.Y);
|
||
SelectedDefect.Scale(scaleX, 1f);
|
||
SelectedDefect.Move(-point.X, -point.Y);
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 竖向缩放比例改变时触发
|
||
/// </summary>
|
||
private void YScaleChange()
|
||
{
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
float scaleY = YScale / SelectedDefect.LastSetScale[2];
|
||
SelectedDefect.LastSetScale[2] = YScale;
|
||
SelectedDefect.YDScale = YScale;
|
||
|
||
var point = SelectedDefect._defectCenterPoint;
|
||
SelectedDefect.Move(point.X, point.Y);
|
||
SelectedDefect.Scale(1f, scaleY);
|
||
SelectedDefect.Move(-point.X, -point.Y);
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自适应图片,缩放到符合控件尺寸
|
||
/// </summary>
|
||
private void FitImage()
|
||
{
|
||
if (null == _bitmap)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_matrix = new Matrix();
|
||
|
||
try
|
||
{
|
||
// 先将图片缩放到适配控件尺寸
|
||
// 宽高比例中的较大值
|
||
float wRatio = 1f * _bitmap.Width / Width;
|
||
float hRatio = 1f * _bitmap.Height / Height;
|
||
float ratio = 1 / Math.Max(wRatio, hRatio);
|
||
|
||
|
||
_matrix.Scale(ratio, ratio);
|
||
_width = (int)(_bitmap.Width * ratio);
|
||
_height = (int)(_bitmap.Height * ratio);
|
||
|
||
// 再将图片平移到控件中心
|
||
// 将plMain的中心转换为图片坐标
|
||
PointF pControlCenter = new(Width / 2.0f, Height / 2.0f);
|
||
PointF pImgCoordinate = pControlCenter.ToImageCoordinate(_matrix);
|
||
|
||
//目标坐标减去当前坐标
|
||
_matrix.Translate(pImgCoordinate.X - _bitmap.Width / 2.0f,
|
||
pImgCoordinate.Y - _bitmap.Height / 2.0f);
|
||
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
|
||
//强制控件使其工作区无效并立即重绘自己和任何子控件
|
||
Invalidate();
|
||
}
|
||
|
||
private void Canvas_SizeChanged(object sender, EventArgs e)
|
||
{
|
||
Refresh();
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//{
|
||
// { MouseButtons.Left, ImageMoveFunc()},
|
||
|
||
// { MouseButtons.Right, ImageMoveFunc},
|
||
|
||
//};
|
||
|
||
// _mouseEventDic
|
||
|
||
public enum ActionEnum
|
||
{
|
||
MOVE_ACTION,
|
||
Move_Down,
|
||
Move_Up,
|
||
|
||
}
|
||
|
||
|
||
public void Set(MouseButtons btn, ActionEnum action)
|
||
{
|
||
switch (action)
|
||
{
|
||
case ActionEnum.MOVE_ACTION:
|
||
_mouseEventDic[btn] = ImageMoveFunc;
|
||
break;
|
||
case ActionEnum.Move_Up:
|
||
_mouseEventDic[btn] = ImageUPFunc;
|
||
break;
|
||
case ActionEnum.Move_Down:
|
||
_mouseEventDic[btn] = ImageDownFunc;
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
private void ImageMoveFunc(object sender, MouseEventArgs e)
|
||
{
|
||
//鼠标左键移动触发移动事件
|
||
if (MouseButtons.Left == e.Button)
|
||
{
|
||
|
||
if (PointF.Empty == _panBasePoint)
|
||
{
|
||
return;
|
||
}
|
||
|
||
switch (_clickArea)
|
||
{
|
||
//case ClickArea.AREA_DEFECT: // 点击了缺陷区域
|
||
// {
|
||
|
||
// // 选中的缺陷
|
||
// if (null == SelectedDefect)
|
||
// {
|
||
// break;
|
||
// }
|
||
|
||
// var tmpMatrix = _matrix.Clone();
|
||
// tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
|
||
// //MouseEventArgs.Location包含相对于控件左上角的point坐标
|
||
// //得到点击的坐标在缺陷坐标系中的坐标值
|
||
// PointF p = e.Location.ToImageCoordinate(tmpMatrix);
|
||
|
||
// float x = p.X - _panBasePoint.X;
|
||
// float y = p.Y - _panBasePoint.Y;
|
||
// Rectangle rectangle = new Rectangle((int)_panBasePoint.X, (int)_panBasePoint.Y,(int)x,(int)y);
|
||
|
||
// // 位移矩阵左乘缺陷矩阵
|
||
// SelectedDefect.Move(x, y);
|
||
|
||
// // 更新缺陷在控件坐标系中的位移
|
||
// PointF nowPoint = SelectedDefect._defectCenterPoint.ToControlCoordinate(tmpMatrix);
|
||
// PointF initialPoint = SelectedDefect.InitialPosition.ToControlCoordinate(_matrix);
|
||
// SelectedDefect.XDMove = nowPoint.X - initialPoint.X;
|
||
// SelectedDefect.YDMove = nowPoint.Y - initialPoint.Y;
|
||
// break;
|
||
// }
|
||
|
||
|
||
|
||
case ClickArea.AREA_IMG: // 点击了图像区域
|
||
{
|
||
PointF p = e.Location.ToImageCoordinate(_matrix);
|
||
|
||
float x = p.X - _panBasePoint.X;
|
||
float y = p.Y - _panBasePoint.Y;
|
||
_matrix.Translate(x, y);
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
private void ImageUPFunc(object sender, MouseEventArgs e)
|
||
{
|
||
if (MouseButtons.Left == e.Button || MouseButtons.Right == e.Button)
|
||
{
|
||
_panBasePoint = Point.Empty;
|
||
_clickArea = ClickArea.AREA_UNKNOW;
|
||
}
|
||
}
|
||
|
||
|
||
private void ImageDownFunc(object sender, MouseEventArgs e)
|
||
{
|
||
Func<Defect, bool> funcContain = defect =>
|
||
{
|
||
// 每个图片的最终矩阵,即背景图的矩阵 右乘 缺陷自身的矩阵
|
||
var tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(defect.PrivateMatrix);
|
||
// 得到点击的坐标在每个缺陷的坐标系中的坐标值
|
||
PointF tmpP = e.Location.ToImageCoordinate(tmpMatrix);
|
||
|
||
if (defect.ContainPoint(tmpP.ToPoint())) // 点击了缺陷区域
|
||
{
|
||
_clickArea = ClickArea.AREA_DEFECT;
|
||
_panBasePoint = tmpP;
|
||
a = e.Location;
|
||
// 此时基准点的坐标系为缺陷图片的坐标系
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
if (AllowSelectDefect)
|
||
{
|
||
if (MouseButtons.Left == e.Button)
|
||
{
|
||
// 如果允许选中缺陷功能
|
||
// 检查是否在缺陷范围内,倒序查找
|
||
var ret = DefectList.LastOrDefault(d => funcContain(d));
|
||
if (null != ret) // 点击了缺陷
|
||
{
|
||
if (ret.Selected) // 点击的是已经选择过的缺陷
|
||
{
|
||
|
||
}
|
||
else // 点击的是未选择的缺陷
|
||
{
|
||
// 取消选择所有的,并选中当前点击的
|
||
DefectList.ForEach(d => d.UnSelect());
|
||
ret.Select();
|
||
}
|
||
return;
|
||
}
|
||
|
||
else
|
||
{
|
||
DefectList.ForEach(d => d.UnSelect());
|
||
|
||
PointF clickPoint = e.Location.ToImageCoordinate(_matrix);
|
||
if (_rcImg.Contains(clickPoint.ToPoint()))
|
||
{
|
||
_clickArea = ClickArea.AREA_IMG;
|
||
_panBasePoint = clickPoint;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (e.Button == MouseButtons.Right)
|
||
{
|
||
PointF clickPoint = e.Location.ToImageCoordinate(_matrix);
|
||
if (_rcImg.Contains(clickPoint.ToPoint()))
|
||
{
|
||
if (null == SelectedDefect)
|
||
{
|
||
return;
|
||
}
|
||
_clickArea = ClickArea.AREA_IMG;
|
||
_panBasePoint = clickPoint.ToImageCoordinate(SelectedDefect.PrivateMatrix);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 图像移动与旋转
|
||
/// </summary>
|
||
private void Canvas_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
//鼠标左键移动触发移动事件
|
||
if (MouseButtons.Left == e.Button)
|
||
{
|
||
//_mouseEventDic[MouseButtons.Left].Invoke(sender, e);
|
||
|
||
if (PointF.Empty == _panBasePoint)
|
||
{
|
||
return;
|
||
}
|
||
|
||
switch (_clickArea)
|
||
{
|
||
//case ClickArea.AREA_DEFECT: // 点击了缺陷区域
|
||
// {
|
||
|
||
// // 选中的缺陷
|
||
// if (null == SelectedDefect)
|
||
// {
|
||
// break;
|
||
// }
|
||
|
||
// var tmpMatrix = _matrix.Clone();
|
||
// tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
|
||
// //MouseEventArgs.Location包含相对于控件左上角的point坐标
|
||
// //得到点击的坐标在缺陷坐标系中的坐标值
|
||
// PointF p = e.Location.ToImageCoordinate(tmpMatrix);
|
||
|
||
// float x = p.X - _panBasePoint.X;
|
||
// float y = p.Y - _panBasePoint.Y;
|
||
|
||
// // 位移矩阵左乘缺陷矩阵
|
||
// SelectedDefect.Move(x, y);
|
||
|
||
// // 更新缺陷在控件坐标系中的位移
|
||
// PointF nowPoint = SelectedDefect._defectCenterPoint.ToControlCoordinate(tmpMatrix);
|
||
// PointF initialPoint = SelectedDefect.InitialPosition.ToControlCoordinate(_matrix);
|
||
// SelectedDefect.XDMove = nowPoint.X - initialPoint.X;
|
||
// SelectedDefect.YDMove = nowPoint.Y - initialPoint.Y;
|
||
// break;
|
||
// }
|
||
|
||
|
||
|
||
case ClickArea.AREA_IMG: // 点击了图像区域
|
||
{
|
||
PointF p = e.Location.ToImageCoordinate(_matrix);
|
||
string log = "x:" + p.X.ToString() + "Y:" + p.Y.ToString();
|
||
WriteLog(log);
|
||
float x = p.X - _panBasePoint.X;
|
||
float y = p.Y - _panBasePoint.Y;
|
||
string log2 = "mmmx:" + x.ToString() + "mmmY:" + y.ToString();
|
||
WriteLog(log2);
|
||
//Rectangle rectangle = new Rectangle((int)_panBasePoint.X, (int)_panBasePoint.Y, (int)x, (int)y);
|
||
// _path = new GraphicsPath();
|
||
//_path.AddRectangle(rectangle);
|
||
//_path.CloseAllFigures();
|
||
|
||
|
||
// Defect defect = new Defect(rectangle, _matrixcs);
|
||
// DefectList.Add(defect);
|
||
//PointF[] ps = new PointF[] { defect._defectCenterPoint };
|
||
// defect.PrivateMatrix.TransformPoints(ps);
|
||
// defect.InitialPosition = ps[0];
|
||
// defect.Select();
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
//鼠标右键移动触发旋转事件
|
||
if (e.Button == MouseButtons.Right)
|
||
{
|
||
if (_panBasePoint == PointF.Empty || _clickArea == ClickArea.AREA_UNKNOW)
|
||
{
|
||
return;
|
||
}
|
||
switch (_clickArea)
|
||
{
|
||
case ClickArea.AREA_UNKNOW:
|
||
case ClickArea.AREA_DEFECT:
|
||
{
|
||
break;
|
||
}
|
||
case ClickArea.AREA_IMG:
|
||
{
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Matrix tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
|
||
PointF p = e.Location.ToImageCoordinate(tmpMatrix);
|
||
|
||
float xBefore = _panBasePoint.X - SelectedDefect._defectCenterPoint.X;
|
||
float yBefore = _panBasePoint.Y - SelectedDefect._defectCenterPoint.Y;
|
||
float xAfter = p.X - SelectedDefect._defectCenterPoint.X;
|
||
float yAfter = p.Y - SelectedDefect._defectCenterPoint.Y;
|
||
|
||
double coorBefore = Math.Atan2(yBefore, xBefore);
|
||
double coorAfter = Math.Atan2(yAfter, xAfter);
|
||
|
||
SelectedDefect.DRoute = ((coorAfter - coorBefore) * 180 / Math.PI);
|
||
Route = SelectedDefect.DRoute;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 角度变化触发
|
||
/// </summary>
|
||
private void DRouteChange()
|
||
{
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
SelectedDefect.RotateAt((float)Route, SelectedDefect._defectCenterPoint);
|
||
SelectedDefect.TotalDRoute += Route;
|
||
Refresh();
|
||
}
|
||
|
||
private PointF a;
|
||
|
||
/// <summary>
|
||
/// 横向位移触发
|
||
/// </summary>
|
||
private void XMoveChange()
|
||
{
|
||
if (null == SelectedDefect)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Matrix tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
SelectedDefect.LastSetPosition = SelectedDefect._defectCenterPoint.ToControlCoordinate(tmpMatrix);
|
||
PointF nowPoint = new PointF(SelectedDefect.LastSetPosition.X + XMove, SelectedDefect.LastSetPosition.Y);
|
||
nowPoint = nowPoint.ToImageCoordinate(tmpMatrix);
|
||
float x = nowPoint.X - SelectedDefect._defectCenterPoint.X;
|
||
float y = nowPoint.Y - SelectedDefect._defectCenterPoint.Y;
|
||
SelectedDefect.Move(x, y);
|
||
SelectedDefect.XDMove += XMove;
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 竖向位移触发
|
||
/// </summary>
|
||
private void YMoveChange()
|
||
{
|
||
if (null == SelectedDefect)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Matrix tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(SelectedDefect.PrivateMatrix);
|
||
SelectedDefect.LastSetPosition = SelectedDefect._defectCenterPoint.ToControlCoordinate(tmpMatrix);
|
||
PointF nowPoint = new PointF(SelectedDefect.LastSetPosition.X, SelectedDefect.LastSetPosition.Y + YMove);
|
||
nowPoint = nowPoint.ToImageCoordinate(tmpMatrix);
|
||
int x = (int)(nowPoint.X - SelectedDefect._defectCenterPoint.X);
|
||
int y = (int)(nowPoint.Y - SelectedDefect._defectCenterPoint.Y);
|
||
SelectedDefect.Move(x, y);
|
||
SelectedDefect.YDMove += YMove;
|
||
Refresh();
|
||
}
|
||
|
||
|
||
public void ColorChange()
|
||
{
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标按下事件
|
||
/// </summary>
|
||
private void Canvas_MouseDown(object sender, MouseEventArgs e)
|
||
{
|
||
|
||
Func<Defect, bool> funcContain = defect =>
|
||
{
|
||
// 每个图片的最终矩阵,即背景图的矩阵 右乘 缺陷自身的矩阵
|
||
var tmpMatrix = _matrix.Clone();
|
||
tmpMatrix.Multiply(defect.PrivateMatrix);
|
||
// 得到点击的坐标在每个缺陷的坐标系中的坐标值
|
||
PointF tmpP = e.Location.ToImageCoordinate(tmpMatrix);
|
||
|
||
if (defect.ContainPoint(tmpP.ToPoint())) // 点击了缺陷区域
|
||
{
|
||
_clickArea = ClickArea.AREA_DEFECT;
|
||
_panBasePoint = tmpP;
|
||
a = e.Location;
|
||
// 此时基准点的坐标系为缺陷图片的坐标系
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
if (AllowSelectDefect)
|
||
{
|
||
if (MouseButtons.Left == e.Button)
|
||
{
|
||
// 如果允许选中缺陷功能
|
||
// 检查是否在缺陷范围内,倒序查找
|
||
var ret = DefectList.LastOrDefault(d => funcContain(d));
|
||
if (null != ret) // 点击了缺陷
|
||
{
|
||
if (ret.Selected) // 点击的是已经选择过的缺陷
|
||
{
|
||
|
||
}
|
||
else // 点击的是未选择的缺陷
|
||
{
|
||
// 取消选择所有的,并选中当前点击的
|
||
DefectList.ForEach(d => d.UnSelect());
|
||
ret.Select();
|
||
}
|
||
return;
|
||
}
|
||
|
||
else
|
||
{
|
||
//Defect rectitem=new Defect();
|
||
// DefectList.Add()
|
||
DefectList.ForEach(d => d.UnSelect());
|
||
|
||
PointF clickPoint = e.Location.ToImageCoordinate(_matrix);
|
||
if (_rcImg.Contains(clickPoint.ToPoint()))
|
||
{
|
||
_clickArea = ClickArea.AREA_IMG;
|
||
_panBasePoint = clickPoint;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (e.Button == MouseButtons.Right)
|
||
{
|
||
PointF clickPoint = e.Location.ToImageCoordinate(_matrix);
|
||
if (_rcImg.Contains(clickPoint.ToPoint()))
|
||
{
|
||
if (null == SelectedDefect)
|
||
{
|
||
return;
|
||
}
|
||
_clickArea = ClickArea.AREA_IMG;
|
||
_panBasePoint = clickPoint.ToImageCoordinate(SelectedDefect.PrivateMatrix);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 鼠标松开的事件
|
||
/// </summary>
|
||
private void Canvas_MouseUp(object sender, MouseEventArgs e)
|
||
{
|
||
if (MouseButtons.Left == e.Button || MouseButtons.Right == e.Button)
|
||
{
|
||
PointF p = e.Location.ToImageCoordinate(_matrix);
|
||
string log = "x:" + p.X.ToString() + "Y:" + p.Y.ToString();
|
||
WriteLog(log);
|
||
float x = p.X - _panBasePoint.X;
|
||
float y = p.Y - _panBasePoint.Y;
|
||
string log2 = "mmmx:" + x.ToString() + "mmmY:" + y.ToString();
|
||
WriteLog(log2);
|
||
//Rectangle rectangle = new Rectangle((int)_panBasePoint.X, (int)_panBasePoint.Y, (int)x, (int)y);
|
||
//GraphicsPath path = new GraphicsPath();
|
||
//path.AddRectangle(rectangle);
|
||
//path.CloseAllFigures();
|
||
//GPathList.Add(path);
|
||
//Matrix _matrixcs = new Matrix();
|
||
//Defect defect = new Defect(rectangle, _matrixcs);
|
||
//DefectList.Add(defect);
|
||
//PointF[] ps = new PointF[] { defect._defectCenterPoint };
|
||
//defect.PrivateMatrix.TransformPoints(ps);
|
||
//defect.InitialPosition = ps[0];
|
||
//defect.Select();
|
||
_panBasePoint = Point.Empty;
|
||
_clickArea = ClickArea.AREA_UNKNOW;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 载入缺陷预览图
|
||
/// </summary>
|
||
public void LoadDefect(string file, Polygon p)
|
||
{
|
||
_path = null;
|
||
PointF[] points = p.GetPoints().ToArray();
|
||
_path = new GraphicsPath();
|
||
_path.AddPolygon(points);
|
||
_path.CloseAllFigures();
|
||
ImagePath = file;
|
||
}
|
||
|
||
public void LoadDefect(string file)
|
||
{
|
||
_path = null;
|
||
ImagePath = file;
|
||
}
|
||
|
||
public void LoadPolygon(string file)
|
||
{
|
||
Matrix _matrix = new Matrix();
|
||
_path = null;
|
||
Defect defect = new Defect(file, _matrix);
|
||
DefectList.Add(defect);
|
||
Refresh();
|
||
}
|
||
|
||
public void LoadPolygon(string file, Polygon p)
|
||
{
|
||
Matrix _matrix = new Matrix();
|
||
PointF[] points = p.GetPoints().ToArray();
|
||
Defect defect = new Defect(file, points, _matrix);
|
||
DefectList.Add(defect);
|
||
PointF[] ps = new PointF[] { defect._defectCenterPoint };
|
||
defect.PrivateMatrix.TransformPoints(ps);
|
||
defect.InitialPosition = ps[0];
|
||
Refresh();
|
||
}
|
||
|
||
|
||
private void WriteLog(string log)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {log}");
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 删除新增缺陷
|
||
/// </summary>
|
||
public void deleteDefect()
|
||
{
|
||
var defect = DefectList.FirstOrDefault(d => d.Selected);
|
||
|
||
if (defect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
DefectList.Remove(defect);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按键按下
|
||
/// </summary>
|
||
private void Canvas_KeyDown(object sender, KeyEventArgs e)
|
||
{
|
||
|
||
if (SelectedDefect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
switch (e.KeyCode)
|
||
{
|
||
case Keys.Left:
|
||
{
|
||
SelectedDefect.Move(-MoveStep, 0, MatrixOrder.Append);
|
||
}
|
||
break;
|
||
case Keys.Up:
|
||
{
|
||
SelectedDefect.Move(0, -MoveStep, MatrixOrder.Append);
|
||
}
|
||
break;
|
||
case Keys.Right:
|
||
{
|
||
SelectedDefect.Move(MoveStep, 0, MatrixOrder.Append);
|
||
}
|
||
break;
|
||
case Keys.Down:
|
||
{
|
||
SelectedDefect.Move(0, MoveStep, MatrixOrder.Append);
|
||
}
|
||
break;
|
||
case Keys.Delete:
|
||
{
|
||
DefectList.Remove(SelectedDefect);
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
Refresh();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 按键抬起
|
||
/// </summary>
|
||
private void Canvas_KeyUp(object sender, KeyEventArgs e)
|
||
{
|
||
_keyLastPressed = Keys.None;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 重写 ProcessDialogKey 使控件可以监听方向键与删除键
|
||
/// </summary>
|
||
protected override bool ProcessDialogKey(Keys keyData)
|
||
{
|
||
if (keyData == Keys.Up || keyData == Keys.Down ||
|
||
keyData == Keys.Left || keyData == Keys.Right || keyData == Keys.Delete)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
return base.ProcessDialogKey(keyData);
|
||
}
|
||
}
|
||
}
|
||
} |