今天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):\n"); 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)錯切:
,結果:

