目标是做一个扫描wafer的界面,界面初始化时会根据wafer的直径和die的边长绘制下图中的样子;
单击启动,程序会开始扫描变绿。
并且窗体拉大缩小时,绘制的图形可以跟着放大缩小,(正常wafer有12寸或者8寸的,不缩小屏幕也放不下),刚开始时是计算图形所在容器的size变化倍数,然后乘上wafer的直径和die的边长,重新绘制图形。但是这样算出来的倍数会有小数,计算的时候有误差,肯定是不对的。最后想到绘制图形和放大缩小用同一个Graphics,这样在放大缩小图形时直接操作这个Graphics就可以了。效果如下图。
下面是代码:
先创建一个cell.cs类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ISIE.COMMON
{
public class Cell
{
#region Fields
private Color _backColor;
//private bool _IsBorder;
private Brush _Brush;
private bool _bSelected = false;
private Rectangle _RectangleF;
#endregion
#region Property
public Color BackColor { get { return this._backColor; } set { this._backColor = value; } }
//public bool IsBorder { get { return this._IsBorder; } set { this._IsBorder = value; } }
public Rectangle Rectangle { get { return this._RectangleF; } set { this._RectangleF = value; } }
public Brush Brush { get { return this._Brush; } set { this._Brush = value; } }
public bool Selected { get { return this._bSelected; } set { this._bSelected = value; } }
#endregion
#region Method
public Cell(Rectangle rectangle)
{
this._RectangleF = rectangle;
}
public void Draw(Graphics g)
{
if (_Brush == null)
{
_Brush = new SolidBrush(Color.Gray);
}
if (this.Selected)
{
g.FillRectangle(new SolidBrush(Color.Green), Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height);
}
else
{
g.FillRectangle(_Brush, Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height);
}
g.DrawRectangle(new Pen(Color.Black), Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height);
}
#endregion
}
}
其次是ScannerViewer自定义控件;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ISIE.COMMON
{
public partial class ScannerViewer : UserControl
{
public ScannerViewer()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw
| ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.DoubleBuffered = true;
}
private int _dieSideLength;
private int _waferDiameter;
private Point _startLocation;
private Point _ellipseCenter;
private List<Cell> cells = new List<Cell>();
private int _currentIdx = -1;
private bool isSelect = false;
private int _interval;
private double _mul = 1;
public int DieSideLength { get => _dieSideLength; set => _dieSideLength = value; }
public int WaferDiameter { get => _waferDiameter; set => _waferDiameter = value; }
public Point StartLocation { get => _startLocation; set => _startLocation = value; }
public Point SetEllipseCenter { set => _ellipseCenter = value; }
public int Interval { get { return _interval <= 0 ? 1000 : _interval; } set { _interval = value <= 0 ? 1000 : value; } }
public double Mul { get { return _mul; } set { _mul = value == 0 ? 0.1 : value; } }
public int CellCount { get => cells.Count; }
public int CurrentIdx { get => _currentIdx; set => _currentIdx = value; }
public void Init()
{
_ellipseCenter = new Point(_waferDiameter / 2, _waferDiameter / 2);
}
private bool CellInWafer(Point die_location, Point wafer_location)
{
//判断方块的四个点是否在圆内,如果在,就返回true,否则返回false。
Point dieLeftTop = die_location;
Point dieLeftDown = new System.Drawing.Point(die_location.X, die_location.Y + _dieSideLength);
Point dieRightTop = new System.Drawing.Point(die_location.X + _dieSideLength, die_location.Y);
Point dieRightDown = new System.Drawing.Point(die_location.X + _dieSideLength, die_location.Y + _dieSideLength);
if ((float)Math.Sqrt((float)Math.Pow(wafer_location.X - dieLeftTop.X, 2) + (float)Math.Pow(wafer_location.Y - dieLeftTop.Y, 2)) <= _waferDiameter / 2)
{
if ((float)Math.Sqrt((float)Math.Pow(wafer_location.X - dieLeftDown.X, 2) + (float)Math.Pow(wafer_location.Y - dieLeftDown.Y, 2)) <= _waferDiameter / 2)
{
if ((float)Math.Sqrt((float)Math.Pow(wafer_location.X - dieRightTop.X, 2) + (float)Math.Pow(wafer_location.Y - dieRightTop.Y, 2)) <= _waferDiameter / 2)
{
if ((float)Math.Sqrt((float)Math.Pow(wafer_location.X - dieRightDown.X, 2) + (float)Math.Pow(wafer_location.Y - dieRightDown.Y, 2)) <= _waferDiameter / 2)
return true;
}
}
}
return false;
}
private void ScanViewer_Paint(object sender, PaintEventArgs e)
{
if (_waferDiameter <= 0 || _dieSideLength <= 0)
{
return;
}
cells.Clear();
Init();
Graphics gra = e.Graphics;
Pen p = new Pen(Color.Black, 2);
gra.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
gra.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
//图像放大缩小
gra.ScaleTransform((float) _mul, (float)_mul);
gra.DrawEllipse(p, _startLocation.X, _startLocation.Y, _waferDiameter, _waferDiameter);
int i = _startLocation.X;
for (int j = _startLocation.Y; j < _startLocation.Y + _waferDiameter;)
{
Point die_PoingStart = new Point(i, j);
//判断方块是否在圆里,如果不在就不显示。
if (CellInWafer(die_PoingStart, _ellipseCenter))
{
Rectangle rectangle = new Rectangle(die_PoingStart.X, die_PoingStart.Y, _dieSideLength, _dieSideLength);
Cell c = new Cell(rectangle);
c.Selected = isSelect;
cells.Add(c);
c.Draw(gra);
}
i = i + _dieSideLength;
if (!(i < _startLocation.X + _waferDiameter))
{
i = _startLocation.X;
j = j + _dieSideLength;
}
}
//扫描当前die
if (!(_currentIdx < 0) )
{
StartScan(gra);
}
//因为图像重绘时会覆盖前面已扫描的die,所以需要把前面覆盖的部分再覆盖回去。
if (_currentIdx != 0)
{
SzieChangDraw(gra);
}
}
public void StartScan(Graphics gra)
{
if (_currentIdx >= cells.Count)
{
return;
}
Cell cell = cells[_currentIdx];
cell.BackColor = Color.Green;
cell.Selected = true;
cell.Draw(gra);
}
public void SzieChangDraw(Graphics gra)
{
for (int i = 0; i < _currentIdx; i++)
{
Cell cell = cells[i];
cell.BackColor = Color.Green;
cell.Selected = true;
cell.Draw(gra);
}
}
}
}
最后是SizeableScanViewer 容器控件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.IO;
namespace ISIE.COMMON
{
public partial class SizeableScanViewer : UserControl
{
Color backColor;
private int _waferDiameter = 1;//wafer直径
private int _dieSideLength = 1;//die的边长
private int _interval = 1000;//默认扫描间隔1000ms
private int cellCount = 0;//die的数量
private int currentIdx = 0;//当前扫描die的下标
System.Windows.Forms.Timer timer1;
public override Color BackColor
{
get
{
return backColor;
}
set
{
backColor = value;
}
}
public SizeableScanViewer()
{
InitializeComponent();
timer1 = new System.Windows.Forms.Timer();
timer1.Tick += timer_Tick;
this.BackColor = this.scannerViewer1.BackColor;
}
void adapt()
{
GetXmlParameter();
this.timer1.Interval = _interval;
int Maxlen = Math.Min(this.Width, this.Height);
int x = 0, y = 0;
double mul = Math.Floor((double)(Maxlen * 1f / _waferDiameter) * 10) / 10 - 0.1;
x = (this.Width - Maxlen) / 2;
y = (this.Height - Maxlen) / 2;
this.scannerViewer1.Location = new Point(x, y);
this.scannerViewer1.WaferDiameter = _waferDiameter;
this.scannerViewer1.DieSideLength = _dieSideLength;
this.scannerViewer1.Mul = mul;
this.scannerViewer1.Size = new Size((int)(_waferDiameter * mul), (int)(_waferDiameter * mul));
}
public void timer_Tick(object sender, EventArgs e)
{
if (currentIdx >= cellCount)
{
this.timer1.Enabled = false;
return;
}
this.scannerViewer1.CurrentIdx = currentIdx;
this.scannerViewer1.Invalidate();
currentIdx++;
}
private void SizeableScanViewer_SizeChanged(object sender, EventArgs e)
{
if (this.Width <= 50 || this.Height <= 50)
return;
adapt();
this.scannerViewer1.Invalidate();
}
public void startScan()
{
cellCount = this.scannerViewer1.CellCount;
this.timer1.Enabled = true;
}
public void stopScan()
{
this.timer1.Enabled = false;
}
//读取XML文件,获取wafer,die的信息和扫描速度
private void GetXmlParameter()
{
XmlDocument doc = new XmlDocument();
string path = System.IO.Directory.GetCurrentDirectory();
if (File.Exists(@path + @"\MachineConfig.xml"))
{
doc.Load(@path + @"\MachineConfig.xml");
XmlNode xn = doc.SelectSingleNode("Machine");
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode xn1 in xnl)
{
string Nodename = xn1.Name;
if ("WaferSize".Equals(Nodename))
{
foreach (XmlNode item in xn1)
{
XmlElement xe = (XmlElement)item;
switch (xe.Name)
{
case "WaferXsize":
_waferDiameter = int.Parse(xe.InnerXml);
break;
case "DieXsize":
_dieSideLength = int.Parse(xe.InnerXml);
break;
case "Speed":
double speed = double.Parse(xe.InnerXml);
_interval = (int)(_dieSideLength * 1000 / speed);
break;
}
}
break;
}
}
}
}
}
}
最后最后在界面上实例化和调用SizeableScanViewer 的startScan()和stopScan()就可以了。
当然,代码比较粗糙。因为一直在测试,很多无用的代码也没有删掉,(⊙o⊙)…。先这样吧,
欢迎大家留言讨论。