二维仿射变换笔记
1. 数学基础
1.1 变换矩阵
二维仿射变换使用3x3的齐次坐标矩阵表示:
[a b tx]
[c d ty]
[0 0 1 ]
其中:
- [a b; c d] 是线性变换部分,表示旋转、缩放和错切
- [tx; ty] 是平移部分
- 最后一行 [0 0 1] 是齐次坐标的固定形式
1.2 基本变换
1.2.1 平移变换
将点沿x轴移动tx,沿y轴移动ty:
x' = x + tx
y' = y + ty
变换矩阵:
[1 0 tx]
[0 1 ty]
[0 0 1 ]
1.2.2 旋转变换
将点绕原点旋转角度θ:
x' = x·cosθ - y·sinθ
y' = x·sinθ + y·cosθ
变换矩阵:
[cosθ -sinθ 0]
[sinθ cosθ 0]
[0 0 1]
1.2.3 缩放变换
将点沿x轴缩放sx倍,沿y轴缩放sy倍:
x' = sx·x
y' = sy·y
变换矩阵:
[sx 0 0]
[0 sy 0]
[0 0 1]
1.2.4 错切变换
- 沿x轴错切:
x' = x + kx·y
y' = y
变换矩阵:
[1 kx 0]
[0 1 0]
[0 0 1]
- 沿y轴错切:
x' = x
y' = y + ky·x
变换矩阵:
[1 0 0]
[ky 1 0]
[0 0 1]
1.2.5 反射变换
- 关于x轴反射:
x' = x
y' = -y
变换矩阵:
[1 0 0]
[0 -1 0]
[0 0 1]
- 关于y轴反射:
x' = -x
y' = y
变换矩阵:
[-1 0 0]
[0 1 0]
[0 0 1]
- 关于原点反射:
x' = -x
y' = -y
变换矩阵:
[-1 0 0]
[0 -1 0]
[0 0 1]
- 关于直线y = kx反射:
- 先将直线旋转到x轴
- 关于x轴反射
- 再旋转回原位置
1.3 复合变换
多个变换的组合通过矩阵乘法实现:
M = M1·M2·...·Mn
注意:变换的顺序很重要,通常先缩放,再旋转,最后平移。
1.4 逆变换
对于可逆变换,其逆变换的矩阵是原矩阵的逆:
M^(-1) = [A^(-1) -A^(-1)·t][0 1 ]
其中A是线性变换部分,t是平移部分。
2. 代码实现
2.1 头文件 (affine2d.h)
#ifndef AFFINE2D_H
#define AFFINE2D_H#include <cmath>
#include "point2d.h"
#include "vector2d.h"/*** @brief 二维仿射变换类* * 实现了二维平面中的各种变换,包括:* - 平移变换:将点沿指定方向移动* - 旋转变换:将点绕原点旋转* - 缩放变换:将点沿坐标轴缩放* - 错切变换:将点沿坐标轴错切* - 反射变换:关于坐标轴、原点或任意直线的反射* - 复合变换:多个变换的组合*/
class Affine2D {
public:/*** @brief 默认构造函数,创建单位变换* * 单位变换的矩阵为:* [1 0 0]* [0 1 0]* [0 0 1]*/Affine2D();/*** @brief 使用矩阵元素构造变换* @param a,b,c,d 线性变换部分的4个元素* @param tx,ty 平移部分的2个元素*/Affine2D(double a, double b, double c, double d, double tx, double ty);/*** @brief 创建平移变换* @param tx x方向平移量* @param ty y方向平移量* @return 平移变换*/static Affine2D translation(double tx, double ty);/*** @brief 创建旋转变换* @param angle 旋转角度(弧度)* @return 旋转变换*/static Affine2D rotation(double angle);/*** @brief 创建缩放变换* @param sx x方向缩放因子* @param sy y方向缩放因子* @return 缩放变换* @throw std::invalid_argument 当缩放因子为0时抛出*/static Affine2D scaling(double sx, double sy);/*** @brief 创建沿x轴的错切变换* @param kx x方向错切因子* @return 错切变换*/static Affine2D shearX(double kx);/*** @brief 创建沿y轴的错切变换* @param ky y方向错切因子* @return 错切变换*/static Affine2D shearY(double ky);/*** @brief 创建关于x轴的反射变换* @return 反射变换*/static Affine2D reflectionX();/*** @brief 创建关于y轴的反射变换* @return 反射变换*/static Affine2D reflectionY();/*** @brief 创建关于原点的反射变换* @return 反射变换*/static Affine2D reflectionOrigin();/*** @brief 创建关于直线的反射变换* @param k 直线斜率* @return 反射变换*/static Affine2D reflection(double k);/*** @brief 变换点* @param point 要变换的点* @return 变换后的点*/Point2D transform(const Point2D& point) const;/*** @brief 变换向量* @param vector 要变换的向量* @return 变换后的向量*/Vector2D transform(const Vector2D& vector) const;/*** @brief 与另一个变换组合* @param other 另一个变换* @return 组合后的变换*/Affine2D compose(const Affine2D& other) const;/*** @brief 求逆变换* @return 逆变换* @throw std::runtime_error 当变换不可逆时抛出*/Affine2D inverse() const;// 属性访问器double getA() const { return a; }double getB() const { return b; }double getC() const { return c; }double getD() const { return d; }double getTx() const { return tx; }double getTy() const { return ty; }private:double a, b; // 线性变换部分的第一行double c, d; // 线性变换部分的第二行double tx, ty