9-2 二维旋转与平移矩阵
二维旋转与平移矩阵(2D Rotation and Translation Matrices)
在二维图形变换中,旋转(Rotation)和平移(Translation)是最基本的两种操作。单独使用 2x2 矩阵可以实现旋转,但无法表示平移——因为平移本质上是加法操作,而非乘法。为了在同一次矩阵乘法中同时完成旋转和平移,我们引入齐次坐标(Homogeneous Coordinates):将二维点 (x, y) 表示为 (x, y, 1),从而使用 3x3 矩阵统一描述所有仿射变换(Affine Transformation)。
这一技巧是计算机图形学(Computer Graphics)、机器人学(Robotics)和游戏开发中的基础工具。掌握它之后,任意复杂的二维变换都可以通过矩阵连乘来组合。
齐次坐标(Homogeneous Coordinates)
齐次坐标的核心思想很简单:在二维坐标末尾添加一个分量 1,将 (x, y) 扩展为 (x, y, 1)。这样一来:
- 旋转:用 3x3 矩阵的左上角 2x2 子矩阵表示,第三行和第三列保持不变
- 平移:用 3x3 矩阵的第三列前两行表示,这是 2x2 矩阵做不到的
- 缩放(Scaling):用 3x3 矩阵的对角线元素表示
- 剪切(Shearing):用 3x3 矩阵的非对角线元素表示
关键优势在于:所有这些变换都可以通过 3x3 矩阵乘法来组合。例如,先旋转再平移,只需将两个 3x3 矩阵相乘,再用结果矩阵乘以点的齐次坐标即可。
普通 2D 坐标: 齐次坐标:
(x, y) → (x, y, 1)
2D 点作为列向量:
| x | | x |
| y | → | y |
| 1 |
矩阵变换的一般形式:
| a b tx | | x | | ax + by + tx |
| c d ty | × | y | = | cx + dy + ty |
| 0 0 1 | | 1 | | 1 |
其中左上角的 2x2 子矩阵 [[a,b],[c,d]] 负责旋转、缩放和剪切,右侧的列 [tx, ty] 负责平移。
平移矩阵(Translation Matrix)
平移矩阵(Translation Matrix)的作用是将点沿着 x 轴移动 tx、沿着 y 轴移动 ty:
| 1 0 tx | | x | | x + tx |
| 0 1 ty | × | y | = | y + ty |
| 0 0 1 | | 1 | | 1 |
例如,将点 (2, 3) 平移 tx=5, ty=1:
| 1 0 5 | | 2 | | 2 + 5 | | 7 |
| 0 1 1 | × | 3 | = | 3 + 1 | = | 4 |
| 0 0 1 | | 1 | | 1 | | 1 |
结果为 (7, 4)。齐次坐标使平移变成了矩阵乘法,而非单独的加法操作。
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
// Translate point (x,y) by (tx,ty) using homogeneous coordinates
void translate(double x, double y, double tx, double ty, double& ox, double& oy) {
// Matrix: | 1 0 tx |
// | 0 1 ty |
// | 0 0 1 |
ox = 1 * x + 0 * y + tx * 1;
oy = 0 * x + 1 * y + ty * 1;
}
int main() {
double x = 2, y = 3;
double tx = 5, ty = 1;
double ox, oy;
translate(x, y, tx, ty, ox, oy);
cout << "Original point: (" << x << ", " << y << ")" << endl;
cout << "Translation: tx=" << tx << ", ty=" << ty << endl;
cout << "Result: (" << ox << ", " << oy << ")" << endl;
return 0;
}
C 实现
#include <stdio.h>
// Translate point (x,y) by (tx,ty) using homogeneous coordinates
void translate(double x, double y, double tx, double ty, double* ox, double* oy) {
// Matrix: | 1 0 tx |
// | 0 1 ty |
// | 0 0 1 |
*ox = 1 * x + 0 * y + tx * 1;
*oy = 0 * x + 1 * y + ty * 1;
}
int main() {
double x = 2, y = 3;
double tx = 5, ty = 1;
double ox, oy;
translate(x, y, tx, ty, &ox, &oy);
printf("Original point: (%g, %g)\n", x, y);
printf("Translation: tx=%g, ty=%g\n", tx, ty);
printf("Result: (%g, %g)\n", ox, oy);
return 0;
}
Python 实现
import math
def translate(x, y, tx, ty):
"""Translate point (x,y) by (tx,ty) using homogeneous coordinates."""
# Matrix: | 1 0 tx |
# | 0 1 ty |
# | 0 0 1 |
ox = 1 * x + 0 * y + tx * 1
oy = 0 * x + 1 * y + ty * 1
return ox, oy
x, y = 2, 3
tx, ty = 5, 1
ox, oy = translate(x, y, tx, ty)
print(f"Original point: ({x}, {y})")
print(f"Translation: tx={tx}, ty={ty}")
print(f"Result: ({ox}, {oy})")
Go 实现
package main
import "fmt"
// translate moves point (x,y) by (tx,ty) using homogeneous coordinates
func translate(x, y, tx, ty float64) (float64, float64) {
// Matrix: | 1 0 tx |
// | 0 1 ty |
// | 0 0 1 |
ox := 1*x + 0*y + tx*1
oy := 0*x + 1*y + ty*1
return ox, oy
}
func main() {
x, y := 2.0, 3.0
tx, ty := 5.0, 1.0
ox, oy := translate(x, y, tx, ty)
fmt.Printf("Original point: (%g, %g)\n", x, y)
fmt.Printf("Translation: tx=%g, ty=%g\n", tx, ty)
fmt.Printf("Result: (%g, %g)\n", ox, oy)
}
以上四个版本都将平移操作显式展开为齐次坐标矩阵乘法。虽然直接做加法 x + tx 更简单,但这里刻意使用矩阵形式,是为了与后续的旋转、缩放等变换保持统一的接口——最终所有变换都可以通过 3x3 矩阵乘法来组合。
运行该程序将输出:
Original point: (2, 3)
Translation: tx=5, ty=1
Result: (7, 4)
旋转矩阵(齐次坐标版)
二维旋转矩阵(Rotation Matrix)使用齐次坐标的 3x3 形式如下:
| cosθ -sinθ 0 | | x | | x·cosθ - y·sinθ |
| sinθ cosθ 0 | × | y | = | x·sinθ + y·cosθ |
| 0 0 1 | | 1 | | 1 |
与 2x2 旋转矩阵相比,3x3 版本多了一行一列,旋转的核心计算完全相同。但 3x3 形式可以与平移矩阵相乘组合,这是 2x2 矩阵做不到的。
以 90 度旋转为例:cos(90) = 0, sin(90) = 1,旋转点 (3, 1):
| 0 -1 0 | | 3 | | -1 |
| 1 0 0 | × | 1 | = | 3 |
| 0 0 1 | | 1 | | 1 |
结果为 (-1, 3),即点 (3, 1) 绕原点逆时针旋转 90 度后到达 (-1, 3)。
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
// Rotate point (x,y) by angle (radians) around origin using 3x3 homogeneous matrix
void rotate(double x, double y, double angle, double& ox, double& oy) {
double c = cos(angle);
double s = sin(angle);
// Matrix: | c -s 0 |
// | s c 0 |
// | 0 0 1 |
ox = c * x + (-s) * y;
oy = s * x + c * y;
}
int main() {
double x = 3, y = 1;
double angle = M_PI / 2; // 90 degrees in radians
double ox, oy;
rotate(x, y, angle, ox, oy);
cout << "Original point: (" << x << ", " << y << ")" << endl;
cout << "Rotation angle: 90 degrees" << endl;
cout << "Result: (" << round(ox) << ", " << round(oy) << ")" << endl;
return 0;
}
C 实现
#include <stdio.h>
#include <math.h>
// Rotate point (x,y) by angle (radians) around origin using 3x3 homogeneous matrix
void rotate(double x, double y, double angle, double* ox, double* oy) {
double c = cos(angle);
double s = sin(angle);
// Matrix: | c -s 0 |
// | s c 0 |
// | 0 0 1 |
*ox = c * x + (-s) * y;
*oy = s * x + c * y;
}
int main() {
double x = 3, y = 1;
double angle = M_PI / 2; // 90 degrees in radians
double ox, oy;
rotate(x, y, angle, &ox, &oy);
printf("Original point: (%g, %g)\n", x, y);
printf("Rotation angle: 90 degrees\n");
printf("Result: (%g, %g)\n", round(ox), round(oy));
return 0;
}
Python 实现
import math
def rotate(x, y, angle):
"""Rotate point (x,y) by angle (radians) around origin using 3x3 homogeneous matrix."""
c = math.cos(angle)
s = math.sin(angle)
# Matrix: | c -s 0 |
# | s c 0 |
# | 0 0 1 |
ox = c * x + (-s) * y
oy = s * x + c * y
return ox, oy
x, y = 3, 1
angle = math.pi / 2 # 90 degrees in radians
ox, oy = rotate(x, y, angle)
print(f"Original point: ({x}, {y})")
print(f"Rotation angle: 90 degrees")
print(f"Result: ({round(ox)}, {round(oy)})")
Go 实现
package main
import (
"fmt"
"math"
)
// rotate rotates point (x,y) by angle (radians) around origin using 3x3 homogeneous matrix
func rotate(x, y, angle float64) (float64, float64) {
c := math.Cos(angle)
s := math.Sin(angle)
// Matrix: | c -s 0 |
// | s c 0 |
// | 0 0 1 |
ox := c*x + (-s)*y
oy := s*x + c*y
return ox, oy
}
func main() {
x, y := 3.0, 1.0
angle := math.Pi / 2 // 90 degrees in radians
ox, oy := rotate(x, y, angle)
fmt.Printf("Original point: (%g, %g)\n", x, y)
fmt.Println("Rotation angle: 90 degrees")
fmt.Printf("Result: (%g, %g)\n", math.Round(ox), math.Round(oy))
}
旋转矩阵的核心计算与 2x2 版本完全相同——都是 x' = x*cos - y*sin 和 y' = x*sin + y*cos。3x3 形式的价值在于:它可以与平移矩阵通过矩阵乘法组合,实现"绕任意点旋转"等复合变换。
运行该程序将输出:
Original point: (3, 1)
Rotation angle: 90 degrees
Result: (-1, 3)
组合变换:旋转+平移
齐次坐标最核心的应用就是组合变换(Composite Transformation)。最经典的例子是"绕任意点旋转":将点 (cx, cy) 平移到原点,执行旋转,再平移回去。
数学表达式为:
T(cx,cy) × R(θ) × T(-cx,-cy) × point
展开为矩阵:
| 1 0 cx | | c -s 0 | | 1 0 -cx | | x |
| 0 1 cy | × | s c 0 | × | 0 1 -cy | × | y |
| 0 0 1 | | 0 0 1 | | 0 0 1 | | 1 |
以具体例子说明:将点 (2, 1) 绕中心点 (1, 1) 逆时针旋转 90 度。
Step 1: 平移到原点(减去中心坐标)
T(-1,-1) × (2,1,1) = (1, 0, 1)
Step 2: 绕原点旋转 90 度
R(90) × (1, 0, 1) = (0, 1, 1)
Step 3: 平移回去(加回中心坐标)
T(1,1) × (0, 1, 1) = (1, 2, 1)
结果为 (1, 2)。
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
// 3x3 matrix multiplication: result = A × B
void mat3Multiply(double A[3][3], double B[3][3], double result[3][3]) {
double temp[3][3] = {{0}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
temp[i][j] += A[i][k] * B[k][j];
}
}
}
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result[i][j] = temp[i][j];
}
// Apply 3x3 transformation matrix to point (x,y)
void transformPoint(double M[3][3], double x, double y, double& ox, double& oy) {
double p[3] = {x, y, 1};
ox = M[0][0] * p[0] + M[0][1] * p[1] + M[0][2] * p[2];
oy = M[1][0] * p[0] + M[1][1] * p[1] + M[1][2] * p[2];
}
int main() {
double cx = 1, cy = 1; // Center of rotation
double angle = M_PI / 2; // 90 degrees
double c = cos(angle), s = sin(angle);
// T(cx,cy): translate to center
double T1[3][3] = {{1, 0, cx}, {0, 1, cy}, {0, 0, 1}};
// R(theta): rotate
double R[3][3] = {{c, -s, 0}, {s, c, 0}, {0, 0, 1}};
// T(-cx,-cy): translate to origin
double T2[3][3] = {{1, 0, -cx}, {0, 1, -cy}, {0, 0, 1}};
// Composite: T1 × R × T2
double temp[3][3], composite[3][3];
mat3Multiply(R, T2, temp); // R × T2
mat3Multiply(T1, temp, composite); // T1 × (R × T2)
double x = 2, y = 1;
double ox, oy;
transformPoint(composite, x, y, ox, oy);
cout << "Rotate (" << x << ", " << y << ") 90 deg around (" << cx << ", " << cy << ")" << endl;
cout << "Result: (" << round(ox) << ", " << round(oy) << ")" << endl;
return 0;
}
C 实现
#include <stdio.h>
#include <math.h>
// 3x3 matrix multiplication: result = A * B
void mat3_multiply(double A[3][3], double B[3][3], double result[3][3]) {
double temp[3][3] = {{0}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
temp[i][j] += A[i][k] * B[k][j];
}
}
}
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result[i][j] = temp[i][j];
}
// Apply 3x3 transformation matrix to point (x,y)
void transform_point(double M[3][3], double x, double y, double* ox, double* oy) {
double p[3] = {x, y, 1};
*ox = M[0][0]*p[0] + M[0][1]*p[1] + M[0][2]*p[2];
*oy = M[1][0]*p[0] + M[1][1]*p[1] + M[1][2]*p[2];
}
int main() {
double cx = 1, cy = 1;
double angle = M_PI / 2;
double c = cos(angle), s = sin(angle);
// T(cx,cy): translate to center
double T1[3][3] = {{1, 0, cx}, {0, 1, cy}, {0, 0, 1}};
// R(theta): rotate
double R[3][3] = {{c, -s, 0}, {s, c, 0}, {0, 0, 1}};
// T(-cx,-cy): translate to origin
double T2[3][3] = {{1, 0, -cx}, {0, 1, -cy}, {0, 0, 1}};
// Composite: T1 * R * T2
double temp[3][3], composite[3][3];
mat3_multiply(R, T2, temp);
mat3_multiply(T1, temp, composite);
double x = 2, y = 1;
double ox, oy;
transform_point(composite, x, y, &ox, &oy);
printf("Rotate (%g, %g) 90 deg around (%g, %g)\n", x, y, cx, cy);
printf("Result: (%g, %g)\n", round(ox), round(oy));
return 0;
}
Python 实现
import math
def mat3_multiply(A, B):
"""Multiply two 3x3 matrices."""
result = [[0]*3 for _ in range(3)]
for i in range(3):
for j in range(3):
for k in range(3):
result[i][j] += A[i][k] * B[k][j]
return result
def transform_point(M, x, y):
"""Apply 3x3 transformation matrix to point (x,y)."""
ox = M[0][0]*x + M[0][1]*y + M[0][2]*1
oy = M[1][0]*x + M[1][1]*y + M[1][2]*1
return ox, oy
cx, cy = 1, 1
angle = math.pi / 2
c, s = math.cos(angle), math.sin(angle)
# T(cx,cy): translate to center
T1 = [[1, 0, cx], [0, 1, cy], [0, 0, 1]]
# R(theta): rotate
R = [[c, -s, 0], [s, c, 0], [0, 0, 1]]
# T(-cx,-cy): translate to origin
T2 = [[1, 0, -cx], [0, 1, -cy], [0, 0, 1]]
# Composite: T1 * R * T2
temp = mat3_multiply(R, T2)
composite = mat3_multiply(T1, temp)
x, y = 2, 1
ox, oy = transform_point(composite, x, y)
print(f"Rotate ({x}, {y}) 90 deg around ({cx}, {cy})")
print(f"Result: ({round(ox)}, {round(oy)})")
Go 实现
package main
import (
"fmt"
"math"
)
// mat3Multiply multiplies two 3x3 matrices
func mat3Multiply(A, B [3][3]float64) [3][3]float64 {
var result [3][3]float64
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
result[i][j] += A[i][k] * B[k][j]
}
}
}
return result
}
// transformPoint applies 3x3 transformation matrix to point (x,y)
func transformPoint(M [3][3]float64, x, y float64) (float64, float64) {
ox := M[0][0]*x + M[0][1]*y + M[0][2]*1
oy := M[1][0]*x + M[1][1]*y + M[1][2]*1
return ox, oy
}
func main() {
cx, cy := 1.0, 1.0
angle := math.Pi / 2
c, s := math.Cos(angle), math.Sin(angle)
// T(cx,cy): translate to center
T1 := [3][3]float64{{1, 0, cx}, {0, 1, cy}, {0, 0, 1}}
// R(theta): rotate
R := [3][3]float64{{c, -s, 0}, {s, c, 0}, {0, 0, 1}}
// T(-cx,-cy): translate to origin
T2 := [3][3]float64{{1, 0, -cx}, {0, 1, -cy}, {0, 0, 1}}
// Composite: T1 * R * T2
temp := mat3Multiply(R, T2)
composite := mat3Multiply(T1, temp)
x, y := 2.0, 1.0
ox, oy := transformPoint(composite, x, y)
fmt.Printf("Rotate (%g, %g) 90 deg around (%g, %g)\n", x, y, cx, cy)
fmt.Printf("Result: (%g, %g)\n", math.Round(ox), math.Round(oy))
}
这段代码展示了齐次坐标最核心的应用。三个矩阵按从右到左的顺序作用于点:先 T(-cx,-cy) 将中心移到原点,再 R 执行旋转,最后 T(cx,cy) 移回原位。通过 mat3Multiply 将三个矩阵合并为一个组合矩阵,只需一次矩阵-向量乘法即可完成全部变换。
运行该程序将输出:
Rotate (2, 1) 90 deg around (1, 1)
Result: (1, 2)
组合变换:平移+旋转+缩放
更一般的仿射变换可以同时包含平移(Translation)、旋转(Rotation)和缩放(Scaling)。缩放矩阵(Scale Matrix)的齐次坐标形式为:
| sx 0 0 | | x | | sx * x |
| 0 sy 0 | × | y | = | sy * y |
| 0 0 1 | | 1 | | 1 |
完整的组合变换顺序为:先缩放,再旋转,最后平移。对应的矩阵表达式:
T(tx,ty) × R(θ) × S(sx,sy) × point
矩阵乘法的顺序非常重要:从右到左依次作用于点。先执行最右边的缩放,然后旋转,最后平移。如果改变顺序,结果会完全不同。
以具体例子说明:对点 (1, 0) 先缩放 (2, 3),再旋转 90 度,最后平移 (5, 1)。
Step 1: 缩放
S(2,3) × (1,0,1) = (2, 0, 1)
Step 2: 旋转 90 度
R(90) × (2, 0, 1) = (0, 2, 1)
Step 3: 平移
T(5,1) × (0, 2, 1) = (5, 3, 1)
结果为 (5, 3)。如果先平移再缩放,结果将完全不同。
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
// 3x3 matrix multiplication: result = A * B
void mat3Multiply(double A[3][3], double B[3][3], double result[3][3]) {
double temp[3][3] = {{0}};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
temp[i][j] += A[i][k] * B[k][j];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result[i][j] = temp[i][j];
}
// Apply 3x3 transformation matrix to point (x,y)
void transformPoint(double M[3][3], double x, double y, double& ox, double& oy) {
ox = M[0][0]*x + M[0][1]*y + M[0][2];
oy = M[1][0]*x + M[1][1]*y + M[1][2];
}
int main() {
double tx = 5, ty = 1; // Translation
double angle = M_PI / 2; // 90 degrees
double sx = 2, sy = 3; // Scaling
double c = cos(angle), s = sin(angle);
// T(tx,ty): translate
double T[3][3] = {{1, 0, tx}, {0, 1, ty}, {0, 0, 1}};
// R(theta): rotate
double R[3][3] = {{c, -s, 0}, {s, c, 0}, {0, 0, 1}};
// S(sx,sy): scale
double S[3][3] = {{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}};
// Composite: T * R * S (applied right to left: scale -> rotate -> translate)
double temp[3][3], composite[3][3];
mat3Multiply(R, S, temp); // R * S
mat3Multiply(T, temp, composite); // T * (R * S)
double x = 1, y = 0;
double ox, oy;
transformPoint(composite, x, y, ox, oy);
cout << "Point: (" << x << ", " << y << ")" << endl;
cout << "Scale (" << sx << ", " << sy << "), Rotate 90 deg, Translate (" << tx << ", " << ty << ")" << endl;
cout << "Result: (" << round(ox) << ", " << round(oy) << ")" << endl;
return 0;
}
C 实现
#include <stdio.h>
#include <math.h>
// 3x3 matrix multiplication: result = A * B
void mat3_multiply(double A[3][3], double B[3][3], double result[3][3]) {
double temp[3][3] = {{0}};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
temp[i][j] += A[i][k] * B[k][j];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result[i][j] = temp[i][j];
}
// Apply 3x3 transformation matrix to point (x,y)
void transform_point(double M[3][3], double x, double y, double* ox, double* oy) {
*ox = M[0][0]*x + M[0][1]*y + M[0][2];
*oy = M[1][0]*x + M[1][1]*y + M[1][2];
}
int main() {
double tx = 5, ty = 1;
double angle = M_PI / 2;
double sx = 2, sy = 3;
double c = cos(angle), s = sin(angle);
double T[3][3] = {{1, 0, tx}, {0, 1, ty}, {0, 0, 1}};
double R[3][3] = {{c, -s, 0}, {s, c, 0}, {0, 0, 1}};
double S[3][3] = {{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}};
double temp[3][3], composite[3][3];
mat3_multiply(R, S, temp);
mat3_multiply(T, temp, composite);
double x = 1, y = 0;
double ox, oy;
transform_point(composite, x, y, &ox, &oy);
printf("Point: (%g, %g)\n", x, y);
printf("Scale (%g, %g), Rotate 90 deg, Translate (%g, %g)\n", sx, sy, tx, ty);
printf("Result: (%g, %g)\n", round(ox), round(oy));
return 0;
}
Python 实现
import math
def mat3_multiply(A, B):
"""Multiply two 3x3 matrices."""
result = [[0]*3 for _ in range(3)]
for i in range(3):
for j in range(3):
for k in range(3):
result[i][j] += A[i][k] * B[k][j]
return result
def transform_point(M, x, y):
"""Apply 3x3 transformation matrix to point (x,y)."""
ox = M[0][0]*x + M[0][1]*y + M[0][2]
oy = M[1][0]*x + M[1][1]*y + M[1][2]
return ox, oy
tx, ty = 5, 1
angle = math.pi / 2
sx, sy = 2, 3
c, s = math.cos(angle), math.sin(angle)
T = [[1, 0, tx], [0, 1, ty], [0, 0, 1]]
R = [[c, -s, 0], [s, c, 0], [0, 0, 1]]
S = [[sx, 0, 0], [0, sy, 0], [0, 0, 1]]
# Composite: T * R * S (scale -> rotate -> translate)
temp = mat3_multiply(R, S)
composite = mat3_multiply(T, temp)
x, y = 1, 0
ox, oy = transform_point(composite, x, y)
print(f"Point: ({x}, {y})")
print(f"Scale ({sx}, {sy}), Rotate 90 deg, Translate ({tx}, {ty})")
print(f"Result: ({round(ox)}, {round(oy)})")
Go 实现
package main
import (
"fmt"
"math"
)
// mat3Multiply multiplies two 3x3 matrices
func mat3Multiply(A, B [3][3]float64) [3][3]float64 {
var result [3][3]float64
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
result[i][j] += A[i][k] * B[k][j]
}
}
}
return result
}
// transformPoint applies 3x3 transformation matrix to point (x,y)
func transformPoint(M [3][3]float64, x, y float64) (float64, float64) {
ox := M[0][0]*x + M[0][1]*y + M[0][2]
oy := M[1][0]*x + M[1][1]*y + M[1][2]
return ox, oy
}
func main() {
tx, ty := 5.0, 1.0
angle := math.Pi / 2
sx, sy := 2.0, 3.0
c, s := math.Cos(angle), math.Sin(angle)
T := [3][3]float64{{1, 0, tx}, {0, 1, ty}, {0, 0, 1}}
R := [3][3]float64{{c, -s, 0}, {s, c, 0}, {0, 0, 1}}
S := [3][3]float64{{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}}
// Composite: T * R * S (scale -> rotate -> translate)
temp := mat3Multiply(R, S)
composite := mat3Multiply(T, temp)
x, y := 1.0, 0.0
ox, oy := transformPoint(composite, x, y)
fmt.Printf("Point: (%g, %g)\n", x, y)
fmt.Printf("Scale (%g, %g), Rotate 90 deg, Translate (%g, %g)\n", sx, sy, tx, ty)
fmt.Printf("Result: (%g, %g)\n", math.Round(ox), math.Round(oy))
}
这段代码演示了三种基本变换的组合。关键点在于矩阵乘法的顺序:T * R * S 表示先缩放(S),再旋转(R),最后平移(T)。矩阵从右向左依次作用于点向量。通过预先将三个矩阵合并为一个组合矩阵,无论包含多少步变换,对每个点的处理都只需一次矩阵乘法。
运行该程序将输出:
Point: (1, 0)
Scale (2, 3), Rotate 90 deg, Translate (5, 1)
Result: (5, 3)
完整实现
以下是一个完整的 3x3 齐次坐标矩阵库,支持单位矩阵(Identity)、平移(Translate)、旋转(Rotate)、缩放(Scale)、矩阵乘法(Multiply)和点变换(Transform Point)。最后用一个测试案例展示:将一个三角形绕其中心旋转 45 度。
C++ 实现
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
// 3x3 homogeneous transformation matrix
struct Mat3 {
double m[3][3];
// Identity matrix
static Mat3 identity() {
Mat3 r;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
r.m[i][j] = (i == j) ? 1 : 0;
return r;
}
// Translation matrix
static Mat3 translate(double tx, double ty) {
Mat3 r = identity();
r.m[0][2] = tx;
r.m[1][2] = ty;
return r;
}
// Rotation matrix (angle in radians)
static Mat3 rotate(double angle) {
Mat3 r = identity();
double c = cos(angle), s = sin(angle);
r.m[0][0] = c; r.m[0][1] = -s;
r.m[1][0] = s; r.m[1][1] = c;
return r;
}
// Scale matrix
static Mat3 scale(double sx, double sy) {
Mat3 r = identity();
r.m[0][0] = sx;
r.m[1][1] = sy;
return r;
}
// Matrix multiplication: this * other
Mat3 multiply(const Mat3& other) const {
Mat3 result;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
result.m[i][j] = 0;
for (int k = 0; k < 3; k++)
result.m[i][j] += m[i][k] * other.m[k][j];
}
return result;
}
// Transform point (x,y)
void transform(double x, double y, double& ox, double& oy) const {
ox = m[0][0]*x + m[0][1]*y + m[0][2];
oy = m[1][0]*x + m[1][1]*y + m[1][2];
}
};
int main() {
// Triangle vertices
double tri[3][2] = {{0, 0}, {2, 0}, {1, 2}};
// Compute centroid
double cx = (tri[0][0] + tri[1][0] + tri[2][0]) / 3.0;
double cy = (tri[0][1] + tri[1][1] + tri[2][1]) / 3.0;
cout << fixed << setprecision(4);
cout << "Triangle vertices:" << endl;
for (int i = 0; i < 3; i++)
cout << " (" << tri[i][0] << ", " << tri[i][1] << ")" << endl;
cout << "Centroid: (" << cx << ", " << cy << ")" << endl;
// Rotate 45 degrees around centroid
double angle = M_PI / 4;
Mat3 composite = Mat3::translate(cx, cy)
.multiply(Mat3::rotate(angle))
.multiply(Mat3::translate(-cx, -cy));
cout << "\nRotate 45 deg around centroid:" << endl;
for (int i = 0; i < 3; i++) {
double ox, oy;
composite.transform(tri[i][0], tri[i][1], ox, oy);
cout << " (" << tri[i][0] << ", " << tri[i][1] << ") -> ("
<< ox << ", " << oy << ")" << endl;
}
return 0;
}
C 实现
#include <stdio.h>
#include <math.h>
typedef double Mat3[3][3];
// Set matrix to identity
void mat3_identity(Mat3 m) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
m[i][j] = (i == j) ? 1 : 0;
}
// Set matrix to translation
void mat3_translate(Mat3 m, double tx, double ty) {
mat3_identity(m);
m[0][2] = tx;
m[1][2] = ty;
}
// Set matrix to rotation (angle in radians)
void mat3_rotate(Mat3 m, double angle) {
mat3_identity(m);
double c = cos(angle), s = sin(angle);
m[0][0] = c; m[0][1] = -s;
m[1][0] = s; m[1][1] = c;
}
// Set matrix to scale
void mat3_scale(Mat3 m, double sx, double sy) {
mat3_identity(m);
m[0][0] = sx;
m[1][1] = sy;
}
// Matrix multiplication: result = A * B
void mat3_multiply(Mat3 A, Mat3 B, Mat3 result) {
Mat3 temp = {{0}};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
temp[i][j] += A[i][k] * B[k][j];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result[i][j] = temp[i][j];
}
// Transform point (x,y) by matrix M
void mat3_transform(Mat3 M, double x, double y, double* ox, double* oy) {
*ox = M[0][0]*x + M[0][1]*y + M[0][2];
*oy = M[1][0]*x + M[1][1]*y + M[1][2];
}
int main() {
double tri[3][2] = {{0, 0}, {2, 0}, {1, 2}};
double cx = (tri[0][0] + tri[1][0] + tri[2][0]) / 3.0;
double cy = (tri[0][1] + tri[1][1] + tri[2][1]) / 3.0;
printf("Triangle vertices:\n");
for (int i = 0; i < 3; i++)
printf(" (%g, %g)\n", tri[i][0], tri[i][1]);
printf("Centroid: (%g, %g)\n", cx, cy);
// Rotate 45 degrees around centroid
double angle = M_PI / 4;
Mat3 T1, R, T2, temp, composite;
mat3_translate(T1, cx, cy);
mat3_rotate(R, angle);
mat3_translate(T2, -cx, -cy);
mat3_multiply(R, T2, temp);
mat3_multiply(T1, temp, composite);
printf("\nRotate 45 deg around centroid:\n");
for (int i = 0; i < 3; i++) {
double ox, oy;
mat3_transform(composite, tri[i][0], tri[i][1], &ox, &oy);
printf(" (%g, %g) -> (%.4f, %.4f)\n", tri[i][0], tri[i][1], ox, oy);
}
return 0;
}
Python 实现
import math
class Mat3:
"""3x3 homogeneous transformation matrix."""
def __init__(self):
self.m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
@staticmethod
def identity():
return Mat3()
@staticmethod
def translate(tx, ty):
r = Mat3()
r.m[0][2] = tx
r.m[1][2] = ty
return r
@staticmethod
def rotate(angle):
r = Mat3()
c, s = math.cos(angle), math.sin(angle)
r.m[0][0] = c; r.m[0][1] = -s
r.m[1][0] = s; r.m[1][1] = c
return r
@staticmethod
def scale(sx, sy):
r = Mat3()
r.m[0][0] = sx
r.m[1][1] = sy
return r
def multiply(self, other):
result = Mat3()
for i in range(3):
for j in range(3):
result.m[i][j] = sum(
self.m[i][k] * other.m[k][j] for k in range(3)
)
return result
def transform(self, x, y):
ox = self.m[0][0]*x + self.m[0][1]*y + self.m[0][2]
oy = self.m[1][0]*x + self.m[1][1]*y + self.m[1][2]
return ox, oy
# Triangle vertices
tri = [(0, 0), (2, 0), (1, 2)]
cx = sum(p[0] for p in tri) / 3
cy = sum(p[1] for p in tri) / 3
print("Triangle vertices:")
for p in tri:
print(f" {p}")
print(f"Centroid: ({cx:.4f}, {cy:.4f})")
# Rotate 45 degrees around centroid
angle = math.pi / 4
composite = Mat3.translate(cx, cy) \
.multiply(Mat3.rotate(angle)) \
.multiply(Mat3.translate(-cx, -cy))
print("\nRotate 45 deg around centroid:")
for px, py in tri:
ox, oy = composite.transform(px, py)
print(f" ({px}, {py}) -> ({ox:.4f}, {oy:.4f})")
Go 实现
package main
import (
"fmt"
"math"
)
// Mat3 represents a 3x3 homogeneous transformation matrix
type Mat3 [3][3]float64
// mat3Identity returns the identity matrix
func mat3Identity() Mat3 {
return Mat3{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
}
// mat3Translate returns a translation matrix
func mat3Translate(tx, ty float64) Mat3 {
m := mat3Identity()
m[0][2] = tx
m[1][2] = ty
return m
}
// mat3Rotate returns a rotation matrix (angle in radians)
func mat3Rotate(angle float64) Mat3 {
m := mat3Identity()
c, s := math.Cos(angle), math.Sin(angle)
m[0][0] = c; m[0][1] = -s
m[1][0] = s; m[1][1] = c
return m
}
// mat3Scale returns a scale matrix
func mat3Scale(sx, sy float64) Mat3 {
m := mat3Identity()
m[0][0] = sx
m[1][1] = sy
return m
}
// multiply returns the product of two 3x3 matrices
func (a Mat3) multiply(b Mat3) Mat3 {
var result Mat3
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
result[i][j] += a[i][k] * b[k][j]
}
}
}
return result
}
// transform applies the matrix to point (x,y)
func (m Mat3) transform(x, y float64) (float64, float64) {
ox := m[0][0]*x + m[0][1]*y + m[0][2]
oy := m[1][0]*x + m[1][1]*y + m[1][2]
return ox, oy
}
func main() {
// Triangle vertices
tri := [3][2]float64{{0, 0}, {2, 0}, {1, 2}}
cx := (tri[0][0] + tri[1][0] + tri[2][0]) / 3
cy := (tri[0][1] + tri[1][1] + tri[2][1]) / 3
fmt.Println("Triangle vertices:")
for _, p := range tri {
fmt.Printf(" (%g, %g)\n", p[0], p[1])
}
fmt.Printf("Centroid: (%.4f, %.4f)\n", cx, cy)
// Rotate 45 degrees around centroid
angle := math.Pi / 4
composite := mat3Translate(cx, cy).
multiply(mat3Rotate(angle)).
multiply(mat3Translate(-cx, -cy))
fmt.Println("\nRotate 45 deg around centroid:")
for _, p := range tri {
ox, oy := composite.transform(p[0], p[1])
fmt.Printf(" (%g, %g) -> (%.4f, %.4f)\n", p[0], p[1], ox, oy)
}
}
以上四个版本分别用 C++ 的结构体+静态方法、C 的函数+二维数组、Python 的类以及 Go 的类型别名实现了完整的 3x3 矩阵库。每个版本都提供了 identity、translate、rotate、scale、multiply 和 transform 六个核心操作。测试案例将三角形绕其重心旋转 45 度,验证了"平移到原点-旋转-平移回去"这一经典组合变换模式。
运行该程序将输出:
Triangle vertices:
(0, 0)
(2, 0)
(1, 2)
Centroid: (1.0000, 0.6667)
Rotate 45 deg around centroid:
(0, 0) -> (0.7618, 1.1785)
(2, 0) -> (1.7618, -0.0355)
(1, 2) -> (0.4764, 0.8569)
二维旋转与平移矩阵的性质
3x3 齐次坐标矩阵的优势
使用 3x3 齐次坐标矩阵(Homogeneous Coordinate Matrix)进行二维变换有以下核心优势:
- 统一表示:旋转、平移、缩放、剪切都可以用 3x3 矩阵表示,无需为不同类型的变换设计不同的数据结构
- 矩阵组合:多个变换可以通过矩阵乘法预先合并为一个矩阵,无论包含多少步变换,对每个点的处理都只需一次矩阵乘法
- 硬件友好:GPU 的图形管线(Graphics Pipeline)直接使用 4x4 齐次坐标处理 3D 变换,2D 的 3x3 是其自然简化
- 逆变换简单:3x3 仿射变换矩阵的逆矩阵仍然是 3x3 仿射变换矩阵,可以高效计算
仿射变换的性质
所有由 3x3 齐次坐标矩阵表示的变换都属于仿射变换(Affine Transformation)。仿射变换具有以下保持性质:
- 共线性(Collinearity):变换前在同一条直线上的点,变换后仍在同一条直线上
- 平行性(Parallelism):变换前平行的直线,变换后仍然平行
- 距离比(Ratios of Distances):同一条直线上各线段长度的比值保持不变
仿射变换不保持的性质:
- 长度和角度一般会改变(除非只包含刚体变换——旋转和平移)
- 面积可能改变(缩放会改变面积,剪切也会改变面积)
变换顺序的重要性
矩阵乘法不满足交换律,因此变换的顺序至关重要:
R(90) * T(1,0) != T(1,0) * R(90)
先平移再旋转:点先移到 (1,0),再绕原点旋转到 (0,1)。先旋转再平移:点先旋转(绕原点旋转无变化),再平移到 (1,0)。结果完全不同。
一般规则:从右到左应用变换。矩阵表达式 T * R * S * point 表示先缩放(S),再旋转(R),最后平移(T)。
所有二维仿射变换的统一形式
任何二维仿射变换都可以表示为一个 3x3 矩阵:
| a b tx |
| c d ty |
| 0 0 1 |
其中:
[a, b, c, d]构成左上角 2x2 子矩阵,负责旋转、缩放和剪切[tx, ty]是平移分量- 第三行始终为
[0, 0, 1],确保仿射变换的封闭性
常见变换的矩阵参数:
| 变换类型 | a | b | c | d | tx | ty |
|---|---|---|---|---|---|---|
| 单位矩阵 | 1 | 0 | 0 | 1 | 0 | 0 |
| 平移 | 1 | 0 | 0 | 1 | tx | ty |
| 旋转 | cos | -sin | sin | cos | 0 | 0 |
| 缩放 | sx | 0 | 0 | sy | 0 | 0 |
| 水平剪切 | 1 | kx | 0 | 1 | 0 | 0 |
| 垂直剪切 | 1 | 0 | ky | 1 | 0 | 0 |
通过矩阵乘法将多个变换组合后,最终得到的仍然是一个 3x3 矩阵。这意味着无论变换序列有多复杂,都可以用一次矩阵乘法完成所有变换。

浙公网安备 33010602011771号