C# GDI+绘图
来源:互联网 发布:大数据平台技术架构 编辑:程序博客网 时间:2024/05/16 15:05
C# GDI+绘图
Graphics
e.Graphics.在窗体或控件的 Paint 事件中接收对图形对象的引用,作为 PaintEventArgs 的一部分。 在为控件创建绘制代码时,通常会使用此方法来获取对图形对象的引用。 同样,您也可以在对对象的PaintEventHandler编程或处理 PrintDocument 的 PrintPage 事件时获取作为 PrintPageEventArgs 的属性的图形对象。
control.CreateGraphics(),调用某控件或窗体的 CreateGraphics 方法以获取对 Graphics 对象的引用,该对象表示该控件或窗体的绘图图面。 如果想在已存在的窗体或控件上绘图,请使用此方法。
从 Image 继承的任何对象创建 Graphics 对象。 此方法在您需要更改已存在的图像时十分有用。
坐标系
主要介绍两种坐标系Page Coordinates 和 World Coordinates.
Page 页面坐标是控件的坐标系,下称老坐标系。一般地,page坐标系是原点在页面(控件)左上角,x轴竖直向下,y轴水平向右的左手坐标系。
设备坐标系是在其上进行绘制的物理设备(如屏幕,纸张)所使用的坐标系。设备坐标的单位只有像素,而页面坐标单位可以设置。在默认情形下,页面坐标和设备坐标相同
World 世界坐标是自己定义的任意的坐标系,也称用户坐标系,建模坐标系,下称新坐标系。建模坐标可以任意设置。
“世界变换”可以将世界坐标系转换为页面坐标系,而“页面变换”可以将页面坐标转换为设备坐标。一般我们用到的是世界变换。
世界变换的矩阵保存在Graphics.Transform 中。是Matrix(System.Drawing.Drawing2D.Matrix)对象。
坐标系的变换
默认的坐标系的变换是坐标变换。设变换的矩阵为P,新基=老基*P
,P是从老坐标系到新坐标系的变换阵。老坐标=P*新坐标
。P即是世界变换的矩阵。
结合性
设从依次进行三个变换P1,P2,P3,那么在默认状态下,结合性为prepend。
新基=老基*P1*P2*P3,老坐标=P1*P2*P3*新坐标
。
从点变换的角度看:变换后的坐标=P1*P2*P3*变换前的坐标
。这种结合性非常不理想,
所以微软又提供了一种结合性append。变换后的坐标=P3*P2*P1*变换前的坐标
,这种结合性非常理想,只要将世界坐标系里面点的坐标一步一步变到第四象限(从世界坐标系的角度看)里面的页面坐标系的区域即可。
由于世界坐标系一般为右手系,一般在最后一步加上 g.ScaleTransform(1, -1, System.Drawing.Drawing2D.MatrixOrder.Append)
即可变为左手系。
所以对于变换矩阵(初始变换矩阵为I)来讲,prepend相当于右乘(I*P),append相当于左乘(P*I),当然,我们更喜欢左乘。
为啥右乘叫prepend,左乘叫append?因为微软的坐标是行向量!,所以从我们的角度来看,相当于
y'=x'P'
,(以’表示转置,以x,y表示(点)变换前后的对应点的坐标(列)向量)这样来看y'=x'*P1'*P2'*P3'
,这样来看,确实是在后面乘,把线性代数中以列向量表示坐标的左乘叫称append也就不足为奇了。为什么变换的顺序十分重要?因为矩阵乘法没有交换律。(特殊情形下如果矩阵乘法满足交换律,则称这两个矩阵是可交换的。
Matrix
基于微软喜欢行向量,所以二维仿射变换的齐次矩阵也被转置了一下。微软的Matrix定义如下
Matrix=[[m11 m12 0], [m21 m22 0], [dx dy 1]]
构造函数
public Matrix(float m11,float m12,float m21,float m22,float dx,float dy)public Matrix(RectangleF,PointF[])
其中第二种方法是求出将一个矩形变换成平行四边形的变换阵。
矩形由RectangleF定义,平行四边形由左上,右上,左下三个点的数组定义。
Maritx的一些方法与Graphics的相应的Transform对应。矩阵的乘相当于变换的复合。
下面对其中主要的几种方法作简要介绍
public void Scale(float scaleX,float scaleY) //对应 m11,m22public void Shear(float shearX,float shearY)// m21 m12public void Translate(float offsetX,float offsetY)//dx dy
全局变换和局部变换
全局变换是应用于给定的Graphics对象绘制的每个项目的变换。
与此相反,局部变换是应用于要绘制的特定项目的变换GraphicsPath。
应用时要先创建GraphicsPath对象,再通过Add…方式在上面作图,可以通过Graphics.DrawPath方法将该对象的所有项目进行绘制,通过Graphics.FillPath方法对GraphicsPath上的所有项目进行填充。
矢量数据的显示
下面用append方式讲解
- 将要显示的区域扩大一点,因为在PictureBox边缘的项目容易被挡住而显示不完整。
- 将扩大后的显示区域平移到第一象限原点附近。
- 将第二步的结果缩放到合适位置。
- 反射到第四象限的绘图区域(页面坐标系)
这个实现的顺序是2 1 4 3,将就着看
public float ViewPort2Page(Graphics g, RectangleF viewport, int w, int h) { g.ResetTransform(); g.TranslateTransform((-viewport.X + 0.01f * viewport.Width), (-(viewport.Y + viewport.Height) - 0.01f * viewport.Height), MatrixOrder.Append); g.ScaleTransform(1, -1, MatrixOrder.Append); float sx = 1.02f * viewport.Width / w, sy = 1.02f * viewport.Height / h; float dx = 0, dy = 0; if (sx != 0 && sy != 0) if (sx < sy) { sx = sy; g.ScaleTransform(1 / sy, 1 / sy, MatrixOrder.Append); dx = (w - viewport.Width * 1.01f / sx) / 2f; } else { sy = sx; g.ScaleTransform(1 / sx, 1 / sx, MatrixOrder.Append); dy = (h - viewport.Height * 1.01f / sy) / 2f; } g.TranslateTransform(dx, dy, MatrixOrder.Append); return sx; }
Q:为啥要返回一个缩放因子s(这里因为sx=sy,将其统一记为s)?
A: 因为将页面坐标系缩放之后,如果再按默认宽度(1 pixel)画图,可能会显示得非常大,将这个默认宽度乘以s即可得到想要的正常的宽度。
杂项
下面称要显示的区域为视口viewport,其由一个RectangleF 来实现。表示世界坐标系中要显示的区域。
平移即视口的平移,可以想象成视口在世界坐标系里的滑动。
单点缩放即视口的缩放,可以理解为视口以这个点为基点放大缩小。
开窗放大即视口的新建,可以理解为以所画矩形(窗)为新的视口。需要注意的是,从屏幕上的开窗放大一般伴随着坐标的变换。
多图层的绘制策略:先绘制面状要素,再绘制线状要素,最后绘制点状要素。
既然世界变换的矩阵可以将世界坐标变为页面坐标,那么对该矩阵求逆(Invert方法)即可将页面坐标变为世界坐标(TransformPoints方法可以进行点数组的转换)。
MouseWheel事件使用时需要在主窗体的load事件中加上
DrawPanel.MouseWheel += new MouseEventHandler(DrawPanel_MouseWheel);e.Delta>0 向上滚,e.Delta<0 向下滚
开窗放大和平移会用到MouseDown,MouseMove,MouseUp事件。其中e.X,e.Y获取鼠标的设备坐标。e.Buttton获取按下键的枚举值有(MouseButtons.Middle)等。
参考资料
- 微软MSDN官方文档
- 课件GDI+(Graphics类)中的坐标
- c# GDI+简单绘图
- c# GDI+简单绘图
- c# GDI+简单绘图
- c# GDI+简单绘图
- c# GDI+简单绘图
- C# GDI+ 简单绘图
- c# GDI+简单绘图
- C# GDI+简单绘图
- C# GDI+ 简单绘图
- C# GDI+ 简单绘图
- C# GDI+ 简单绘图
- C# GDI+ 简单绘图
- C# GDI+简单绘图
- C# GDI+ 绘图
- c# GDI+简单绘图
- C# GDI+ 绘图
- C# GDI+绘图
- C# GDI+简单绘图二
- 数据结构实验之排序四:寻找大富翁
- 支持向量机原理(理解SVM的三层境界)
- 顺序+每次迭代,顺序+每次出现
- 高新集训D3D4总结
- 橡皮怪Dici,over,welcome,player
- C# GDI+绘图
- git语法
- 修改Anaconda中的Jupyter Notebook默认工作路径
- C语言趣味一百道 第20题 2017_12_22
- FWT
- Python第四课 列表及元组
- 12.16三道题
- VR、AR、MR的区别
- mvn常用命令