今天yogurt和大家分享的是计算机图形学里算是最基础的一个内容——二维空间里的简单矩形变换,变换方式包括平移、按比例缩放、旋转、对称和错切。下一章yogurt分享了《三维空间里的简单的长方体透视变换》http://www.cnblogs.com/to-sunshine/p/6501208.html,想要进一步学习图形的各种变换的同学可以去简单学习一下。那么,按照惯例yogurt又要给大家普及知识点啦~~
---------------------------------------------------------yogurt小课堂开课啦--------------------------------------------------
在线性代数里我们学过矩阵的基本运算,它在空间图形的变换中起着尤为关键的作用。我们把二维空间的每一个点的坐标记为(x,y,1),三维空间的每一个点的坐标记为(x,y,z,1);对于二维空间其变换矩阵是一个3X3的矩阵:;而三维空间的变换矩阵是一个4X4的矩阵:,具体的矩阵乘法怎么计算的在这里我就不讲啦。
对于二维空间来说,各种变换矩阵如下:
(1)平移:,有: * = ;
(2)按比例缩放:,有 * = ;
(3)旋转:,该点绕原点O逆时针旋转了θ角度。
(4)对称:
以X轴对称:,有 * = ;
以Y轴对称:,有 * = ;
以原点O对称:,有 * = ;
以Y=X对称:,有 * = ;
以Y=-X对称:,有 * = ;
(5)错切:,有 * = ;
-------------------------------------------------------------下课啦---------------------------------------------------------------
接下来,yogurt说一下程序的整体思路就直接上代码啦:
首先在主函数里,是用户依次输入原始矩形的四个点后,程序会将该原始矩形绘制出来;然后根据用户输入的变换需求,程序进入到不同的变换函数中。每一个变换函数会根据自己的变换性质提示用户输入控制变换的数据,然后程序生成对应的变换矩阵,将矩形的每一个点与变换矩阵做矩阵乘法即可得到每一个点在变换后的坐标。最后根据变换后的坐标画出变换后的矩阵即可。
代码如下:(我直接使用了我老师给的画图程序,"Graph.cpp”)
// bianhuan.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include"Graph.h" #include<math.h> #define PI 3.1415926 typedef struct POint { int a[3]; }point; point change(point p, int a[3][3]) { int b[3] = { 0, 0, 0 }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) b[i] += p.a[j] * a[j][i]; } p.a[0] = b[0]; p.a[1] = b[1]; p.a[2] = b[2]; return p; } point change2(point p, double a[3][3]) { double b[3] = { 0, 0, 0 }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) b[i] += p.a[j] * a[j][i]; } p.a[0] = b[0]; p.a[1] = b[1]; p.a[2] = b[2]; return p; } void DrawXY() { setPenColor(WHITE); moveTo(0, 0); lineTo(230, 0); drawText(200, 0,"230"); moveTo(0, 0); lineTo(0, 230); drawText(0, 230, "230"); return; } void DrawRectangle(point p1, point p2, point p3, point p4) { setPenColor(0xFF00); //需要注意一下变换点输入顺序也要可以画出矩形来 moveTo(p1.a[0], p1.a[1]); lineTo(p2.a[0], p2.a[1]); lineTo(p3.a[0], p3.a[1]); lineTo(p4.a[0], p4.a[1]); lineTo(p1.a[0], p1.a[1]); return; } void DrawChange(point p1, point p2, point p3, point p4, int a[3][3]) { point p11 = change(p1, a); point p22 = change(p2, a); point p33 = change(p3, a); point p44 = change(p4, a); clearWindow(); DrawXY(); DrawRectangle(p11, p22, p33, p44); return; } void DrawChange2(point p1, point p2, point p3, point p4, double a[3][3]) { point p11 = change2(p1, a); point p22 = change2(p2, a); point p33 = change2(p3, a); point p44 = change2(p4, a); clearWindow(); DrawXY(); DrawRectangle(p11, p22, p33, p44); return; } void Translation(point p1, point p2, point p3, point p4) { int tx, ty; printf("Please enter the change of x and y(格式:dx dy):"); scanf("%d %d", &tx, &ty); int a[3][3] = { 1, 0, 0, 0, 1, 0, tx, ty, 1 }; DrawChange(p1, p2, p3, p4, a); return; } void Scale(point p1, point p2, point p3, point p4) { int sx, sy; printf("Please enter the change of x and y(格式:dx dy):"); scanf("%d %d", &sx, &sy); int a[3][3] = { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; DrawChange(p1, p2, p3, p4, a); return; } void Rotation(point p1, point p2, point p3, point p4) { double d; printf("Please enter the angel of rotation:(格式:60度就输入60):"); scanf("%lf", &d); getchar(); d = d / 180 * 1.0 * PI; double a[3][3] = { cos(d), -sin(d), 0, sin(d), cos(d), 0, 0, 0, 1 }; DrawChange2(p1, p2, p3, p4, a); return; } void Symmetry(point p1, point p2, point p3, point p4) { char mark; printf("Please enter the change:(以什么为中心做对称,输入:x/y/o(原点)/a(y=x)/b(y=-x))"); scanf("%c", &mark); getchar(); int a[3][3] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; switch (mark) { case'x': a[1][1] = -1; DrawChange(p1, p2, p3, p4, a); break; case'y': a[0][0] = -1; DrawChange(p1, p2, p3, p4, a); break; case'o': a[0][0] = a[1][1] = -1; DrawChange(p1, p2, p3, p4, a); break; case'a': a[0][0] = a[1][1] = 0; a[0][1] = a[1][0] = 1; DrawChange(p1, p2, p3, p4, a); break; case'b': a[0][0] = a[1][1] = 0; a[0][1] = a[1][0] = -1; DrawChange(p1, p2, p3, p4, a); break; default: break; } return; } void Shear(point p1, point p2, point p3, point p4) { int b, c; printf("Please enter the change of x and y(格式:dx dy):"); scanf("%d %d", &b, &c); int a[3][3] = { 1, b, 0, c, 1, 0, 0, 0, 1 }; DrawChange(p1, p2, p3, p4, a); } int _tmain(int argc, _TCHAR* argv[]) { point p1, p2, p3, p4; char order; printf("Please enter four point coordinationa(格式:x1,y1 x2,y2 x3,y3 x4,y4): "); scanf("%d,%d %d,%d %d,%d %d,%d", &p1.a[0], &p1.a[1], &p2.a[0], &p2.a[1], &p3.a[0], &p3.a[1], &p4.a[0], &p4.a[1]); p1.a[2] = p2.a[2] = p3.a[2] = p4.a[2] = 1; DrawXY(); DrawRectangle(p1, p2, p3, p4); getchar(); while (1) { printf("Please enter your control order:(平移:p,比例缩放:b,旋转:x,对称:d,错切:c,退出:q)"); scanf("%c", &order); getchar(); switch (order) { case'p':Translation(p1, p2, p3, p4); break; case'b':Scale(p1, p2, p3, p4); break; case'x':Rotation(p1, p2, p3, p4); break; case'd':Symmetry(p1, p2, p3, p4); break; case'c':Shear(p1, p2, p3, p4); break; case'q':return 0; default: break; } } return 0; }
我们来看看结果,验证一下代码的正确性吧:
1、用户输入矩形坐标:
,程序画出原始矩形:
2、输入变换需求并进行变换:
(1)平移:
,结果:
(2)按比例缩放:
,结果:
(3)旋转:
,结果:
(4)对称:
以X轴做对称:
以Y轴做对称:
以原点做对称:
以Y=X做对称:
以Y=-X做对称:
(5)错切:
,结果: