Windows GDI+坐标系统详解(一)
2015-12-02 21:30
399 查看
原文出自http://blog.csdn.net/smstong/article/details/6405482
GDI+中的坐标系
![](https://img-blog.csdn.net/20151202211813411?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2 坐标系变换与矩阵运算
既然可以找到描述形状方便的坐标系,那么问题也来了。比如要同时描述两个形状,如两个圆,而且这两个圆是有相对位置的,比如是自行车的两个轮子。
![](https://img-blog.csdn.net/20151202211932847?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
尽管两个圆各自在自己的坐标系里都能很方便的描述,但是要建立两者之间的关系时,却遇到了麻烦。因为要计算两个圆的位置关系,必须把两个圆放到同一个坐标系下描述才行。所以就引出了坐标系变换的概念。在此例中,可以把第二个圆也放到第一个坐标系下描述,方法就是把第二个坐标系放到第一个坐标系中合适的位置(两个坐标系的关系),然后根据两个坐标系的关系,推算出第二个圆在第一个坐标系中的描述。
这种方式对于CAD中的任务分隔特别重要,比如做汽车设计的公司,可以把不同的部件分配给不同的人来做。设计人员接到任务后,自由选择合适的坐标系来描述负责的部件。等所有部件设计完成以后,再把所有的部件转换的整车坐标系上。坐标系间的转换(2维和3维)是非常有规律的,有数学基础的人可以自己推导公式,没有数学基础的也没有关系,各种图形库都已经把坐标系变换公式做成了函数API供程序调用。比如OpenGL提供了三维坐标系间的各种变换API,GDI+则提供了2维坐标的变换API。需要了解的是,坐标系间的变换,一般是通过矩阵运算完成的,感兴趣的读者可以参考任何讲解OpenGL坐标变换算法的书籍,重要的是矩阵运算可以通过硬件流水线完成,这就是图形显示中的显卡硬件加速的一部分。当然矩阵运算不光应用于坐标系转换,还广泛运用于其他计算领域,因此有人提出了用GPU代替CPU来进行大规模科学计算的方案。
3 GDI+中的三种坐标系
作为Windows中图形显示的关键部件,GDI+代表了Windows下2维图形API。三维则是D3D的领域了。图形API要提供的函数大概是两类,一是绘图函数,二是坐标系转换函数。GDI+提供了很多绘图函数,如DrawRectangle,DrawEclipse,DrawString等等。所有这些函数中都需要位置或大小参数,对于这些参数含义的理解是很重要的。
3.1 调用者自定义坐标系(world)
一是参数的单位是什么?位置参数的坐标系是什么?答案很有意思:不确定。因为这些东西有调用者自由确定。那么GDI+怎么根据这些不确定的参数绘制图形呢?答案是调用者要提供自己定义的坐标系和PAGE坐标系的关系。
3.2 Page坐标系
Page坐标系附属在某一个窗口或控件上,是一个固定的坐标系,原点位于窗口的左上角,x轴方向向右,y轴方向向下。单位为cm,inch或pixel,根据实际情况设定。GDI+提供了Page坐标系和World坐标系间的转换API。含义是把world坐标系放到Page坐标系合适的位置。回到前面讲过的汽车分部件设计的例子,此处Page坐标系就是最后的整车坐标系,GID+提供的就是把各个部件(GDI+绘制函数绘制的图形)连同其坐标系一起放到整车(Page)坐标系里。
![](https://img-blog.csdn.net/20151202212109338?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这是很合理的方式。在利用GDI+作图时也要按照这种思路来做。具体说来,先把整个图形分解成各个小的图形,在画某一个小的图形时不要考虑它最终在Page坐标系的位置,只要按照你自己设想的坐标系来调用GDI+的绘图函数就可以了。
当所有的图形都绘制完毕后,在把这些小的图形统统放到Page坐标系里。具体就是,调用绘制小图形的代码之前调用GDI+的xxxTransform()系列函数把小图形的建模坐标系放置到Page坐标系里,在绘制小图形的代码之后,调用ResetTransform()。
![](https://img-blog.csdn.net/20151202212213159?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
讲到这里,也许大家会有疑问了,GDI+最后是如何把Page坐标系的图形绘制到屏幕上的呢,这就是显示器的Device坐标系。
![](https://img-blog.csdn.net/20151202212519246?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
4.2 分析
仔细看上面的图形,不难发现,此图形有6部分组成:头,左臂,右臂,身体,左腿,右腿。分别把各个部分分给6个设计师去建模,然后把各个模型连同其建模坐标系一起放到到Page坐标系中。如下图:
![](https://img-blog.csdn.net/20151202212152954?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
1 什么是坐标系
坐标系就是确定一组数据位置的标尺。按按照维数分为2维平面坐标系和3维空间坐标系。其实2维坐标系也是z=0的3维坐标系的特例。
坐标系有三要素,一是原点,二是方向,三是单位大小。如果两个坐标系这三点完全一样,那么这两个坐标系就完全相同。关于坐标系和点的关系,我们可以这么理解:点本身是固定的,但在不同坐标系下的表示是不同的。那么为什么要定义那么多的坐标系呢,答案是为了描述方便。比如描述一个圆,如果把坐标系原点放到圆心,那么对圆的描述就是 x2+y2=r2。而如果原点不在圆心,那么圆描述就成了:(x-x0)2+(y-y0)2=r2。
2 坐标系变换与矩阵运算
既然可以找到描述形状方便的坐标系,那么问题也来了。比如要同时描述两个形状,如两个圆,而且这两个圆是有相对位置的,比如是自行车的两个轮子。
尽管两个圆各自在自己的坐标系里都能很方便的描述,但是要建立两者之间的关系时,却遇到了麻烦。因为要计算两个圆的位置关系,必须把两个圆放到同一个坐标系下描述才行。所以就引出了坐标系变换的概念。在此例中,可以把第二个圆也放到第一个坐标系下描述,方法就是把第二个坐标系放到第一个坐标系中合适的位置(两个坐标系的关系),然后根据两个坐标系的关系,推算出第二个圆在第一个坐标系中的描述。
这种方式对于CAD中的任务分隔特别重要,比如做汽车设计的公司,可以把不同的部件分配给不同的人来做。设计人员接到任务后,自由选择合适的坐标系来描述负责的部件。等所有部件设计完成以后,再把所有的部件转换的整车坐标系上。坐标系间的转换(2维和3维)是非常有规律的,有数学基础的人可以自己推导公式,没有数学基础的也没有关系,各种图形库都已经把坐标系变换公式做成了函数API供程序调用。比如OpenGL提供了三维坐标系间的各种变换API,GDI+则提供了2维坐标的变换API。需要了解的是,坐标系间的变换,一般是通过矩阵运算完成的,感兴趣的读者可以参考任何讲解OpenGL坐标变换算法的书籍,重要的是矩阵运算可以通过硬件流水线完成,这就是图形显示中的显卡硬件加速的一部分。当然矩阵运算不光应用于坐标系转换,还广泛运用于其他计算领域,因此有人提出了用GPU代替CPU来进行大规模科学计算的方案。
3 GDI+中的三种坐标系
作为Windows中图形显示的关键部件,GDI+代表了Windows下2维图形API。三维则是D3D的领域了。图形API要提供的函数大概是两类,一是绘图函数,二是坐标系转换函数。GDI+提供了很多绘图函数,如DrawRectangle,DrawEclipse,DrawString等等。所有这些函数中都需要位置或大小参数,对于这些参数含义的理解是很重要的。
3.1 调用者自定义坐标系(world)
一是参数的单位是什么?位置参数的坐标系是什么?答案很有意思:不确定。因为这些东西有调用者自由确定。那么GDI+怎么根据这些不确定的参数绘制图形呢?答案是调用者要提供自己定义的坐标系和PAGE坐标系的关系。
3.2 Page坐标系
Page坐标系附属在某一个窗口或控件上,是一个固定的坐标系,原点位于窗口的左上角,x轴方向向右,y轴方向向下。单位为cm,inch或pixel,根据实际情况设定。GDI+提供了Page坐标系和World坐标系间的转换API。含义是把world坐标系放到Page坐标系合适的位置。回到前面讲过的汽车分部件设计的例子,此处Page坐标系就是最后的整车坐标系,GID+提供的就是把各个部件(GDI+绘制函数绘制的图形)连同其坐标系一起放到整车(Page)坐标系里。
这是很合理的方式。在利用GDI+作图时也要按照这种思路来做。具体说来,先把整个图形分解成各个小的图形,在画某一个小的图形时不要考虑它最终在Page坐标系的位置,只要按照你自己设想的坐标系来调用GDI+的绘图函数就可以了。
当所有的图形都绘制完毕后,在把这些小的图形统统放到Page坐标系里。具体就是,调用绘制小图形的代码之前调用GDI+的xxxTransform()系列函数把小图形的建模坐标系放置到Page坐标系里,在绘制小图形的代码之后,调用ResetTransform()。
讲到这里,也许大家会有疑问了,GDI+最后是如何把Page坐标系的图形绘制到屏幕上的呢,这就是显示器的Device坐标系。
3.3 Device坐标系
对于Page坐标系和Device坐标系的转换,应用程序员不需要了解了,GDI+已经把这部分隐藏了。4 GDI+中坐标系的转换实例
4.1题目
利用GDI+绘制如下图形:4.2 分析
仔细看上面的图形,不难发现,此图形有6部分组成:头,左臂,右臂,身体,左腿,右腿。分别把各个部分分给6个设计师去建模,然后把各个模型连同其建模坐标系一起放到到Page坐标系中。如下图:
4.3代码
private PointF pHead; private PointF pBody; private PointF pLeftArm; private PointF pRightArm; private PointF pLeftLeg; private PointF pRightLeg; private SizeF sHead = new SizeF(30, 30); //头大小30cm private SizeF sBody = new SizeF(50, 70); //身体大小 private SizeF sArm = new SizeF(10, 60); //胳膊大小 private SizeF sLeg = new SizeF(20, 70); //腿大小 public void DrawHead(PaintEventArgs e) { e.Graphics.DrawEllipse(Pens.Red, -sHead.Width / 2.0f, -sHead.Height / 2.0f, sHead.Width, sHead.Height); } public void DrawBody(PaintEventArgs e) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, sBody.Width, sBody.Height); } public void DrawLeftArm(PaintEventArgs e) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, sArm.Width, sArm.Height); } public void DrawRightArm(PaintEventArgs e) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, sArm.Height, sArm.Width); } public void DrawLeftLeg(PaintEventArgs e) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, sLeg.Width, sLeg.Height); } public void DrawRightLeg(PaintEventArgs e) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, sLeg.Height, sLeg.Width); } private void Form1_Paint(object sender, PaintEventArgs e) { pHead = new PointF(this.Width / 2.0f, 100f); //放置头坐标系 e.Graphics.TranslateTransform(pHead.X, pHead.Y); DrawHead(e); //调用负责头建模的代码 e.Graphics.ResetTransform(); //重置矩阵 pBody = new PointF(pHead.X - sBody.Width/2.0f, pHead.Y+sHead.Height/2.0f); e.Graphics.TranslateTransform(pBody.X, pBody.Y); DrawBody(e); e.Graphics.ResetTransform(); pLeftArm = pBody; e.Graphics.TranslateTransform(pLeftArm.X, pLeftArm.Y); e.Graphics.RotateTransform(45); DrawLeftArm(e); e.Graphics.ResetTransform(); pRightArm = new PointF(pBody.X + sBody.Width, pBody.Y); e.Graphics.TranslateTransform(pRightArm.X, pRightArm.Y); e.Graphics.RotateTransform(45); DrawRightArm(e); e.Graphics.ResetTransform(); pLeftLeg = new PointF(pBody.X, pBody.Y + sBody.Height); e.Graphics.TranslateTransform(pLeftLeg.X, pLeftLeg.Y); e.Graphics.RotateTransform(45); DrawLeftLeg(e); e.Graphics.ResetTransform(); pRightLeg = new PointF(pBody.X + sBody.Width, pBody.Y + sBody.Height); e.Graphics.TranslateTransform(pRightLeg.X, pRightLeg.Y); e.Graphics.RotateTransform(45); DrawRightLeg(e); e.Graphics.ResetTransform(); } }
相关文章推荐
- 代码中设置view的大小和位置
- private, public, protected 访问标号的访问范围之C++篇
- java(6)--事件监听机制
- [JSP]标准标签、<jsp:include、forward、param、useBean、setProperty、getProperty>
- 第一个 IronPython 的 ASP.NET 程序
- Ubuntu 修改环境变量
- JavaScript、JSP、Java及javaEE
- Scrum Meeting 1-20151201
- 第一天 简介、安装和目录管理命令
- redis源码阅读(一):开始
- Android-Property 动画自定义TypeEvaluator
- APUE:信号 - 未决信号与信号阻塞
- ubuntu14.04 如何删除客人会话
- Spring IOC容器基本原理
- HDU 3085 Nightmare Ⅱ(双向BFS)
- doGet与doPost的区别
- 人脸识别必读的N篇文章
- Clear Screen and remove objects in RStudio
- 【Java小项目】打飞机
- 《恰同学少年》