第7章 二维几何变换

应用于对象几何描述并改变它的位置、方向或大小的操作称为几何变换(geometric transformation)

几何变换有时也称为建模变换(modeling transformation),但有些图形系统将两者区分开来。建模变换一般用于构造场景或给出由多个部分组合而成的复杂对象的层次式描述等。

基本的二维几何变换

平移、旋转和缩放是所有图形软件包中都包含的几何变换函数。可能包括在图形软件包中的其他变换函数有反射和错切操作。

二维平移

通过将位移量加到一个点的坐标上来生成一个新的坐标位置,可以实现一次平移(translation)。实际上,我们将该点从原始位置沿一直线路径移动到新位置。

将平移距离(translation distance)python 二维坐标的核密度 python二维坐标移动_二维几何变换python 二维坐标的核密度 python二维坐标移动_Python_02加到原始坐标python 二维坐标的核密度 python二维坐标移动_计算机图形学_03上获得一个新的坐标位置python 二维坐标的核密度 python二维坐标移动_计算机图形学_04,可以实现一个二维位置的平移。
python 二维坐标的核密度 python二维坐标移动_Python_05
一对平移距离python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_06称为平移向量(translation vector)或位移向量(shift vector)

python 二维坐标的核密度 python二维坐标移动_Python_07


如果用矩阵表示

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_08

这样就可以使用矩阵形式来表示二维平移方程:

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_09

下面的程序演示了平移操作。输入的平移向量用来将一个多边形的n个顶点从世界坐标系的一个位置移动到另一个位置,而OpenGL子程序用来重新生成平移后的多边形。

class wcPt2D(
	public: GLfloat x,y;
}; 
void translatePolygon (wcPt2D"verts, GLint nVerts, GLfloat tx, GLfloat ty){
	GLint k; 
          for (k=0;k<nVerts:k++){
              verts [k].x = verts [k].x + tx; 
              verts [k].y =verts [k].y + ty; 
          }
           g1Begin (GL_POLYGON); 
                       for (k=0;k< nVerts;k++)
			g1Vertex2f (verts [k].x, verts [k].y); 
           g1End();
}

二维旋转

通过指定一个旋转轴(rotation axis)和一个旋转角度(rotation angle),可以进行一次旋转(rotation)变换。在将对象的所有顶点按指定角度绕指定旋转轴旋转后,该对象的所有点都旋转到新位置。

对象的二维旋转通过在python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面上沿圆路径将对象重定位来实现。此时,我们将对象绕与python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面垂直的旋转轴(与python 二维坐标的核密度 python二维坐标移动_二维_12轴平行)旋转。二维旋转的参数有旋转角python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_13和称为旋转点(rotation point 或pivot point)的位置python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_14,对象绕该点旋转。基准点是旋转轴与python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面的交点。正角度python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_13定义绕基准点的逆时针旋转,而负角度将对象沿顺时针方向旋转。

为了简化模型,我们先假设基准点为原点。

python 二维坐标的核密度 python二维坐标移动_Python_17

因此旋转后用角度python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_13python 二维坐标的核密度 python二维坐标移动_二维几何变换_19表示为
python 二维坐标的核密度 python二维坐标移动_二维_20
在极坐标系中,点的原始坐标为
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_21
所以可以得到:
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_22
如果用矩阵的形式表示:
python 二维坐标的核密度 python二维坐标移动_二维几何变换_23
其中,旋转矩阵为:
python 二维坐标的核密度 python二维坐标移动_二维几何变换_24
但现在OpenGL、Java、PHIGS和GKS都按标准列向量方式表示。

任意的旋转位置python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_14旋转点的变换方程:
python 二维坐标的核密度 python二维坐标移动_二维_26
线段的旋转可以通过用于每个线段端点,并重新绘制新端点间的线段而得到。多边形的旋转则是将每个顶点旋转指定的旋转角,并使用新的顶点来生成多边形而实现旋转。曲线的旋转通过重新定位定义的点并重新绘制曲线而完成。例如圆或椭圆,可以通过将中心位置沿指定旋转角对着的弧移动而绕非中心轴旋转。椭圆可通过旋转其长轴和短轴来实现绕其中心位置的旋转。

class wcPt2D {}
	public:
		GLfloat x,y; 
}; 
void rotatePolygon (wcPt2D* verts, GLint nVerts, wePt2D pivPt, GLdouble theta)
{
    wcPt2D* vertsRot; 
    GLint k; 
    for (k=0;k< nVerta:k++){
	vertsRot [k].x=pivPt.x+(verts [k].x-pivPt.x) * cos (theta)-(verts [k].y-pivpt.y)* sin (theta); 
         vertsRot [k].y=pivPt.y+(verts [k],x-pivPt.x )* sin (theta)+(verts [k].y-pivPt.y)* cos (theta):
    ]
    g1Begin { GL_POLYGON}: 
        for(k=0;k<nVerts;k++)
  		g1Vertex2f (verteRot [k].x, verteRot [k].y): 
    g1End();

二维缩放

改变一个对象的大小,可使用缩放(scaling)变换。一个简单的二维缩放操作可通过将缩放系数(scaling factor)python 二维坐标的核密度 python二维坐标移动_二维几何变换_27python 二维坐标的核密度 python二维坐标移动_计算机图形学_28,与对象坐标位置python 二维坐标的核密度 python二维坐标移动_计算机图形学_03相乘而得:
python 二维坐标的核密度 python二维坐标移动_计算机图形学_30
矩阵形式如下
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_31
或者是
python 二维坐标的核密度 python二维坐标移动_Python_32
python 二维坐标的核密度 python二维坐标移动_二维几何变换_27python 二维坐标的核密度 python二维坐标移动_计算机图形学_28相同的值的时候,就会产生保持对象相对比例一致的一致缩放(uniform scaling)。当两个值不相等的时候就是差值缩放(differential scaling)。

当缩放系数的绝对值小于1时,缩放后的对象向原点靠近;而缩放系数绝对值大于1时,缩放后的坐标位置远离原点。

我们可以选择一个在缩放变换后不改变位置的点,称为固定点(fixed point),以控制缩放后对象的位置。固定点的坐标python 二维坐标的核密度 python二维坐标移动_二维_35可以选择对象的中点等位置或任何其他空间位置。这样,多边形通过缩放每个顶点到固定点的距离而相对于固定点进行缩放。对于坐标为python 二维坐标的核密度 python二维坐标移动_计算机图形学_03的顶点,缩放后的坐标python 二维坐标的核密度 python二维坐标移动_二维_37可计算为
python 二维坐标的核密度 python二维坐标移动_二维_38
可以得到
python 二维坐标的核密度 python二维坐标移动_计算机图形学_39
其中python 二维坐标的核密度 python二维坐标移动_二维_40python 二维坐标的核密度 python二维坐标移动_二维几何变换_41都是常数。

python 二维坐标的核密度 python二维坐标移动_二维几何变换_42

class wePt2D {
    public: 
    GLfloat x,y; 
};
void scalePolygon (wcPt2D· verts, GLint nVerts, wcpt2D fixedPt, GLfloat sx, GLfloat sy)
{
    wcPt2D vertsNew; 
    GLint k; 
    for (k-0;k<nVerts;k++){
        vertsNew[k].x=verts [k].x* sx+ fixedPt.x*(1-sx); 
        vertaNew[k].y=verts [k].y*sy+fixedPt.y*(1: sy): 
         
    } 
    g1Begin (GL POLYGON]; 
             for (k-0;k<nVerte;k++) 
             	g1Vertex2f (vertsNew [k].x, vertsNew [k].y); 
     g1End():

矩阵表示和齐次坐标

每个基本变换(平移、旋转和缩放)都可以表示为普通矩阵形式
python 二维坐标的核密度 python二维坐标移动_二维_43
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_44python 二维坐标的核密度 python二维坐标移动_二维_45都是坐标的列向量。矩阵python 二维坐标的核密度 python二维坐标移动_二维_46是一个包含乘法系数的python 二维坐标的核密度 python二维坐标移动_二维_47的矩阵,python 二维坐标的核密度 python二维坐标移动_二维几何变换_48是包含平移项的两元素列矩阵。

对于平移,python 二维坐标的核密度 python二维坐标移动_二维_46是单位矩阵。对于旋转或缩放,python 二维坐标的核密度 python二维坐标移动_二维几何变换_48包含与基准点或缩放固定点相关的平移项。

齐次坐标

如果将python 二维坐标的核密度 python二维坐标移动_二维_47矩阵表达式扩充为python 二维坐标的核密度 python二维坐标移动_二维_52矩阵,就可以把二维儿何变换的乘法和平移组合成单一矩阵表示。这时将变换矩阵的第三列用于平移项,而所有的变换公式可表达为矩阵乘法。但为了这样操作,必须解释二维坐标位置到三元列向量的矩阵表示。标准的实现技术是将二维坐标位置表示python 二维坐标的核密度 python二维坐标移动_计算机图形学_03扩充到三维表示python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_54,称为齐次坐标(homogeneous coordinate),这里的齐次参数(homogeneous parameter)h是一个非零值,因此
python 二维坐标的核密度 python二维坐标移动_计算机图形学_55
普通的二维齐次坐标表示可写为python 二维坐标的核密度 python二维坐标移动_计算机图形学_56。最简单的python 二维坐标的核密度 python二维坐标移动_Python_57。因此每个二维位置都可以用齐次坐标来表示python 二维坐标的核密度 python二维坐标移动_二维几何变换_58来表示。

二维平移矩阵

使用齐次坐标方法,坐标位置的二维平移可表示为下面的矩阵乘法:
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_59
该平移操作可简写为
python 二维坐标的核密度 python二维坐标移动_二维几何变换_60

二维旋转矩阵

二维的旋转变换公式为
python 二维坐标的核密度 python二维坐标移动_二维_61
其中旋转变换操作python 二维坐标的核密度 python二维坐标移动_二维几何变换_62的旋转参数python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_13

二维缩矩阵

相对于坐标原点的缩放变换可以表示为
python 二维坐标的核密度 python二维坐标移动_二维_64

逆变换

对于平移变换,我们通过对平移距离取负值而得到逆矩阵。因此,如果二维平移距离是python 二维坐标的核密度 python二维坐标移动_二维几何变换python 二维坐标的核密度 python二维坐标移动_Python_02,则其逆平移矩阵是

python 二维坐标的核密度 python二维坐标移动_计算机图形学_67

逆旋转通过用旋转角度的负角取代该旋转角来实现。例如,绕坐标系原点的角度为0的二维旋转有如下的逆变换矩阵
python 二维坐标的核密度 python二维坐标移动_二维_68
将缩放系数用其倒数取代就得到了缩放变换的逆矩阵。对以坐标系原点为中心、缩放参数为python 二维坐标的核密度 python二维坐标移动_二维几何变换_27python 二维坐标的核密度 python二维坐标移动_计算机图形学_28的二维缩放,其逆变换矩阵为
python 二维坐标的核密度 python二维坐标移动_二维_71

二维复合变换

利用矩阵表达式,可以通过计算单个变换的矩阵乘积,将任意的变换序列组成复合变换矩阵(composite transformation matrix)。形成变换矩阵的乘积经常称为矩阵的合并(concatenation) 或复合(composition)。由于一个坐标位置用齐次列矩阵表示,我们必须用表达任意变换顺序的矩阵来前乘该列矩阵。由于场景中许多位置用相同的顺序变换,先将所有变换矩阵相乘形成一个复合矩阵将是高效率的方法。因此,如果我们要对点位置P进行两次变换,变换后的位置将用下式计算:
python 二维坐标的核密度 python二维坐标移动_二维几何变换_72

复合二维平移

假如将两个连续的平移向量python 二维坐标的核密度 python二维坐标移动_二维_73python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_74用于坐标位置P,那么最后的变换位置python 二维坐标的核密度 python二维坐标移动_二维_45可以计算为
python 二维坐标的核密度 python二维坐标移动_二维几何变换_76
这表示两个连续平移是相加的。

复合二维旋转

python 二维坐标的核密度 python二维坐标移动_Python_77

并且
python 二维坐标的核密度 python二维坐标移动_计算机图形学_78

复合二维缩放

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_79

或者是
python 二维坐标的核密度 python二维坐标移动_计算机图形学_80

通用二维基准点旋转

当图形软件包仅提供绕坐标系原点的旋转函数时,我们可通过完成下列平移-旋转-平移操作序列来实现绕任意选定的基准点python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_14的旋转。

  1. 平移对象使基准点位置移动到坐标原点;
  2. 绕坐标原点旋转;
  3. 平移对象使基准点回到其原始位置。

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_82

该等式可以使用下列形式表示:
python 二维坐标的核密度 python二维坐标移动_Python_83
其中python 二维坐标的核密度 python二维坐标移动_二维几何变换_84

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_85

通用二维基准点缩放

在只有相对于坐标原点缩放的缩放函数时,缩放的变换序列:

  1. 平移对象使固定点与坐标原点重合;
  2. 对于坐标原点进行缩放;
  3. 使用步骤1的反向平移将对象返回到原始位置。

python 二维坐标的核密度 python二维坐标移动_计算机图形学_86


python 二维坐标的核密度 python二维坐标移动_二维_87

python 二维坐标的核密度 python二维坐标移动_Python_88

通用二维定向缩放

参数python 二维坐标的核密度 python二维坐标移动_二维几何变换_27python 二维坐标的核密度 python二维坐标移动_计算机图形学_28沿x和y方向缩放对象,可以通过在应用缩放变换之前,将对象所希望的缩放方向旋转到与坐标轴一致来实现在其他方向上缩放对象。

首先完成旋转操作,使python 二维坐标的核密度 python二维坐标移动_Python_91python 二维坐标的核密度 python二维坐标移动_二维_92的方向分别与x和y轴重合。然后应用缩放变换python 二维坐标的核密度 python二维坐标移动_二维_93,再进行反向旋转回到其原始位置。从这三个变换的乘积得到的复合矩阵为
python 二维坐标的核密度 python二维坐标移动_计算机图形学_94

矩阵合并特性

矩阵相乘符合结合律,对于任何三个矩阵python 二维坐标的核密度 python二维坐标移动_二维_46python 二维坐标的核密度 python二维坐标移动_二维几何变换_48python 二维坐标的核密度 python二维坐标移动_二维_97。矩阵积python 二维坐标的核密度 python二维坐标移动_Python_98可以写成
python 二维坐标的核密度 python二维坐标移动_二维_99
因此,依靠变换的描述顺序,我们既可以使用从左到右(前乘),也可以使用从右到左(后乘)的结合分组来求矩阵乘积。有些图形软件包要求变换按应用的顺序描述。

二维刚体变换

如果一个变换矩阵仅包含平移和旋转参数,则它是一个刚体变换矩阵(rigid-body transformation matrix)。二维刚体变换矩阵的一般形式为
python 二维坐标的核密度 python二维坐标移动_二维_100
其中,4个元素python 二维坐标的核密度 python二维坐标移动_Python_101是多重旋转项,元素python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_102python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_103是平移项。坐标位置的刚体变换有时也称为刚体运动(rigid motion)。变换后的坐标位置间的所有角度和距离都不变化。

因此上述左上角的python 二维坐标的核密度 python二维坐标移动_二维_47矩阵是一个正交矩阵(orthogonal matrix)的特性。说明,如果将子矩阵的每一行(或每一列)作为向量,那么两个行向量python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_105python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_106形成单位向量的正交组。这样的一组向量也称为正交向量组。每个向量具有单位长度。
python 二维坐标的核密度 python二维坐标移动_计算机图形学_107
并且向量相互垂直
python 二维坐标的核密度 python二维坐标移动_二维几何变换_108

二维复合矩阵编程

其他二维变换

反射

产生对象镜像的变换称为反射(reflection)。对于二维反射而言,其反射镜像通过将对象绕反射轴旋转python 二维坐标的核密度 python二维坐标移动_Python_109而生成。我们选择的反射轴(axis of reflection)可以是在python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面内的一条直线或者是垂直python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面的一条直线。当反射轴是python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面内的一条直线时,绕这个轴的旋转路径在垂直于python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面的平面中;而对于垂直于python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面的反射轴,旋转路径在python 二维坐标的核密度 python二维坐标移动_二维几何变换_10平面内。下面举出一些普通的反射例子。

关于直线python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_116(python 二维坐标的核密度 python二维坐标移动_二维_117轴)的反射,可以由下列的变换矩阵完成。
python 二维坐标的核密度 python二维坐标移动_计算机图形学_118
对于python 二维坐标的核密度 python二维坐标移动_二维几何变换_119(y轴)的反射,翻动python 二维坐标的核密度 python二维坐标移动_二维_117的坐标而保持python 二维坐标的核密度 python二维坐标移动_二维_121坐标不变,这种变换的矩阵是
python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_122

python 二维坐标的核密度 python二维坐标移动_Python_123


关于python 二维坐标的核密度 python二维坐标移动_二维几何变换_10的平面内任意直线python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_125

通常,我们先平移直线使其经过原点。然后将直线旋转到坐标轴之一,并进行相对于坐标轴的反射。最后利用逆旋转和逆平移变换将直线还原到原来位置。

错切

错切(shear) 是一种使对象形状发生变化的变换,经过错切的对象好像是由已经相互滑动的内部夹层组成。两种常用的错切变换是移动python 二维坐标的核密度 python二维坐标移动_二维_117坐标值的错切和移动python 二维坐标的核密度 python二维坐标移动_二维_121坐标值的错切。

相对于python 二维坐标的核密度 python二维坐标移动_二维_117轴的python 二维坐标的核密度 python二维坐标移动_二维_117方向错切由下列变换矩阵产生:
python 二维坐标的核密度 python二维坐标移动_计算机图形学_130
该矩阵将坐标位置转换成
python 二维坐标的核密度 python二维坐标移动_Python_131
可以将任意实数赋给错切参数python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_132.。然后将坐标位置python 二维坐标的核密度 python二维坐标移动_计算机图形学_03水平地移动与其到python 二维坐标的核密度 python二维坐标移动_二维_117轴的距离(python 二维坐标的核密度 python二维坐标移动_二维_121值)成正比的量。

python 二维坐标的核密度 python二维坐标移动_二维_136


错切操作可以表示为基本变换的序列。

几何变换的光栅方法

光栅系统的特殊功能为特定的二维变换提供了另一种方法。光栅系统将图像信息作为颜色图案存储在帧缓冲器中。控制矩形像素数组的光栅功能通常称为光栅操作(raster operation),将一块像素从一个位置移动到另一个位置的过程也称为像素值的块移动(block transfer,bitblt或pixblt)。图形软件中通常包含完成某些光栅操作的子程序。

90°倍数的旋转可以很容易地利用重新安排像素矩阵的元素而实现。通过首先将阵列的每一行的像素值颠倒,然后交换其行和列来将对象逆时针旋转90°;通过颠倒阵列的每一行中元素的顺序,然后将行的顺序颠倒来得到180°的旋转。

像素块的光栅缩放采用类似方法实现。我们用指定的python 二维坐标的核密度 python二维坐标移动_二维几何变换_27python 二维坐标的核密度 python二维坐标移动_计算机图形学_28值对原始块中的像素区域进行缩放,并将缩放的矩形映射到一组目标像素上,然后按照其与缩放像素区域的重叠区域,设置每个目标像素的亮度。

OpenGL光栅变换

像素颜色值的矩形数组从一个缓存到另一个的平移可以作为如下的OpenGL复制操作来完成:

g1CopyPixe1s (xmin, ymin, width, height. GL_COLOR):

前面4个参数给出了像素块的位置和尺寸。而OpenGL符号常量GL_COLOR指定要复制的颜色值。该像素数组复制到刷新缓存中由当前光栅位置指定的左下角的一个矩形区域内。像素颜色值依赖于颜色模式的当前设定,按RGBA或颜色表进行复制。提供复制的区域(源)和复制目标区域均应位于屏幕坐标边界内。该平移可作用于任何刷新缓存或不同缓存之间。g1CopyPixels函数的源缓存用g1ReadBuffer 子程序选择,而目标缓存用g1DrawBuffer子程序选择。

缓存中的一个RGB颜色块可以用下列函数存入一个数组:

g1ReadPixels (xmin, ymin, width. height, GL_RGB.GL_UNSIGNED_BYTE, colorArray):

如果颜色表索引存于像素位置,则将GL_COLOR_INDEX取代GL_RGB。为了旋转颜色值,必须如前一节所述重新安排颜色数组的行与列。然后使用下列语句将旋转后的数组放回缓存:

g1DrawPixe1s (width,height,GL_RGB,GL_UNSIGNED_BYTE, colorArray);

该数组的左下角放到当前光栅位置。我们用g1ReadBuffer选择包含原来的像素值块的源缓存,用g1DrawBuffer指定目标缓存。

二维缩放变换通过指定缩放因子然后引用g1Copypixe1s或g1Drawpixe1s按OpenGL中的光栅操作来完成。对于光栅操作,使用下列函数来设定缩放因子:

g1Pixe1zoom(sx,sy):

这里,参数sx和sy可赋以任何非零浮点值。大于1.0的正值增太源数组元素的尺对,而小于1.0的正值减少元素尺寸。sx或sy中有负值或两个都为负值则生成该数组元素的反射及缩放。因此,如果sx=sy=-3.0,则源数组相对于当前光栅位置反射且数组的每一颜色元素映射到目标缓存中的3×3像素块。如果目标像素的中心位于一数组缩放颜色元素的矩形区域,则用该数组元素给它赋值。中心在缩放数组元素左边界或上边界的目标像素也赋以该元素的颜色。sx和sy的默认值均为1.0。

二维坐标系间的变换

非笛卡儿系统的例子有极坐标系统、球面坐标系统、椭圆坐标系统和抛物线坐标系统。

给出了一个在笛卡儿坐标系python 二维坐标的核密度 python二维坐标移动_二维几何变换_10中用坐标原点python 二维坐标的核密度 python二维坐标移动_二维几何变换_140及方向角python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_13指定的笛卡儿坐标系python 二维坐标的核密度 python二维坐标移动_二维_142。为了将对象描述从python 二维坐标的核密度 python二维坐标移动_二维几何变换_10坐标变换到python 二维坐标的核密度 python二维坐标移动_二维_142坐标,必须建立把python 二维坐标的核密度 python二维坐标移动_二维_142轴叠加到python 二维坐标的核密度 python二维坐标移动_二维几何变换_10轴的变换,这需要分两步进行:

  1. python 二维坐标的核密度 python二维坐标移动_二维_147系统的坐标原点python 二维坐标的核密度 python二维坐标移动_二维_148平移到python 二维坐标的核密度 python二维坐标移动_计算机图形学_149系统的原点python 二维坐标的核密度 python二维坐标移动_二维几何变换_150
  2. python 二维坐标的核密度 python二维坐标移动_二维几何变换_151轴旋转到python 二维坐标的核密度 python二维坐标移动_二维_152轴上

坐标原点的平移可以使用下列矩阵操作表示:
python 二维坐标的核密度 python二维坐标移动_二维几何变换_153
为了将两个系统的轴重合,可以顺时针旋转:
python 二维坐标的核密度 python二维坐标移动_二维_154
把这两个变换矩阵合并起来,就给出了将对象描述从python 二维坐标的核密度 python二维坐标移动_二维几何变换_10系统转换到python 二维坐标的核密度 python二维坐标移动_二维_142系统的完整复合矩阵。
python 二维坐标的核密度 python二维坐标移动_计算机图形学_157

python 二维坐标的核密度 python二维坐标移动_二维_158

OpenGL二维几何变换函数

在OpenGL的核心库中,每一种基本的几何变换都有一个独立的函数。由于OpenGL是作为三维图形应用编程接口(APl)来设计的,所有变换都在三维空间中定义。在内部,所有坐标均使用4元素列向量表示,而所有变换均使用4×4矩阵表示。因此,二维变换可以通过在OpenGL中选择使第三维(z)不改变的z值来实现。

基本的OpenGL几何变换

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_159平移矩阵用下列子程序构造

g1Translate*(tx,ty,tz)

平移参数tx、ty和tz可赋予任意的实数值,附加于该函数的单个后缀码或者是f(浮点)或者是d。

旋转矩阵python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_159下列函数生成:

glRotate*(theta,vx,vy,vz)

向量python 二维坐标的核密度 python二维坐标移动_二维几何变换_161的分量可以有任意的浮点数值。该向量用于定义通过坐标原点的旋转轴的方向。

用下列函数可得到相对于坐标原点的4×4缩放矩阵:

glScale*(sx, sy, sz)

OpenGL矩阵操作

将该矩阵看做建模观察矩阵(modelview matrix),它用于存储和组合几何变换,也用于将几何变换与向观察坐标系的变换进行组合。建模观察模式用下列语句指定:

g1MatrixMode (GL_MODELVIEW)

该语句指定一个4×4建模观察矩阵作为当前矩阵(current matrix)

在这个调用后的OpenGL变换子程序用来修改建模观察矩阵,而后该矩阵用来变换场景中的坐标位置。用g1MatrixMode函数还可以设定另外两个模式:纹理模式(texture mode)和颜色模式(color mode)。纹理模式用于映射表面的纹理图案,而颜色模式用于从一个颜色模型转换到另一个。后面几章将讨论观察、投影、纹理和颜色变换。

建立建模观察模式(或任何其他模式)后,调用变换子程序所生成的矩阵要与该模式的当前矩阵相乘。另外,我们可以对当前矩阵的元素赋值,OpenGL库中有两个函数可用于此目的。使用下列函数可设定当前矩阵为单位矩阵:

glLoadIdentity():

也可以为当前矩阵的元素赋其他值:

g1LoadMatrix*(elements16):

参数elements16指定了一个单下标、16元素的浮点值数组,而后缀f或d用来指定数据类型。该数组的元素必须按列优先顺序指定。即先列出第一列的4个元数,接着列出第二列的4个元素,然后是第三列,而最后是第四列。

也可以将指定的矩阵与当前矩阵合并:

glMultMatrix*(otherElements16):

实例

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import math

winWidth, winHeight = 600, 600
xwcMin, xwcMax = 0.0, 300.0
ywcMin, ywcMax = 0.0, 300.0
pi = 3.14159
matComposite = [[0.0, 0.0, 0.0],
                [0.0, 0.0, 0.0],
                [0.0, 0.0, 0.0]]

def init():
    glClearColor(1.0, 1.0, 1.0, 0.0)


def get3x3Matfloat():
    temp = [[0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0]]
    return temp


# 矩阵初始化
def matrix3x3SetIndentity(matIdent3x3):
    for row in range(3):
        for col in range(3):
            matIdent3x3[row][col] = (row == col)

# 显示三角形
def triangle(verts):
    glBegin(GL_TRIANGLES)
    for k in range(3):
        glVertex2f(verts[k][0], verts[k][1])
    glEnd()


def matrix3x3PreMultiply(m1, m2):
    mattemp = get3x3Matfloat()
    for row in range(3):
        for col in range(3):
            mattemp[row][col] = m1[row][0] * m2[0][col] + m1[row][1] * m2[1][col] + m1[row][2] * m2[2][col]

    for row in range(3):
        for col in range(3):
            m2[row][col] = mattemp[row][col]

def rotate2D(pivotPt, theta):
    global matComposite
    matRot = get3x3Matfloat()
    matrix3x3SetIndentity(matRot)
    matRot[0][0] = math.cos(theta)
    matRot[0][1] = -math.sin(theta)
    matRot[0][2] = pivotPt[0] * (1-math.cos(theta)) + pivotPt[1] * math.sin(theta)

    matRot[1][0] = math.sin(theta)
    matRot[1][1] = math.cos(theta)
    matRot[1][2] = pivotPt[0] * (1-math.cos(theta)) - pivotPt[1]*math.sin(theta)

    matrix3x3PreMultiply(matRot, matComposite)

def scale2D(sx, sy, fixedPt):
    global matComposite
    matScale = get3x3Matfloat()
    matrix3x3SetIndentity(matScale)
    matScale[0][0] = sx
    matScale[0][2] = (1-sx)*fixedPt[0]
    matScale[1][1] = sy
    matScale[1][2] = (1-sy) * fixedPt[1]
    matrix3x3PreMultiply(matScale, matComposite)


def translate2D(tx, ty):
    global matComposite
    matTrans1 = get3x3Matfloat()
    matrix3x3SetIndentity(matTrans1)
    matTrans1[0][2] = tx
    matTrans1[1][2] = ty
    matrix3x3PreMultiply(matTrans1, matComposite)

def transformVerts2D(nVerts, verts):
    global matComposite
    for k in range(nVerts):
        temp = matComposite[0][0] * verts[k][0] + matComposite[0][1] * verts[k][1] + matComposite[0][2]
        verts[k][1] = matComposite[1][0] * verts[k][0] + matComposite[1][1] * verts[k][1] + matComposite[1][2]
        verts[k][0] = temp

def dispalyFcn():
    global pi,matComposite
    nVerts = 3
    verts = [[50.0, 25.0],[150.0, 25.0],[100.0, 100.0]]
    xsum , ysum = 0,0
    for k in range(nVerts):
        xsum += verts[k][0]
        ysum += verts[k][1]
    centroidPt = [0,0]
    centroidPt[0] = xsum/nVerts
    centroidPt[1] = ysum/nVerts
    pivPt = centroidPt[:]
    fixedPt = centroidPt[:]
    tx = 0.0
    ty = 100.0
    sx = 0.5
    sy = 0.5
    theta = pi/2.0
    glClear(GL_COLOR_BUFFER_BIT)
    glColor3f(0.0, 0.0, 1.0)
    triangle(verts)
    matrix3x3SetIndentity(matComposite)

    scale2D(sx, sy, fixedPt)

    rotate2D(pivPt, theta)

    translate2D(tx, ty)

    transformVerts2D(nVerts, verts)

    glColor3f(1.0, 0.0, 0.0)
    triangle(verts)
    glFlush()



def winReshapeFcn(newWidth, newHeight):
    global xwcMax, xwcMin, ywcMax, ywcMin
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax)
    glClear(GL_COLOR_BUFFER_BIT)


if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
    glutInitWindowPosition(50, 50)
    glutInitWindowSize(winWidth, winHeight)
    glutCreateWindow("几何变换".encode('gbk'))
    init()
    glutDisplayFunc(dispalyFcn)
    glutReshapeFunc(winReshapeFcn)
    glutMainLoop()

python 二维坐标的核密度 python二维坐标移动_python 二维坐标的核密度_162