一些概念
齐次坐标是用n+1维向量表示n维向量,例如点
P
(
x
,
y
)
的齐次化坐标为
(
w
x
,
w
y
,
w
)
,
w
为任意不为
0
的数
P(x,y)的齐次化坐标为(wx,wy,w),w为任意不为0的数
P(x,y)的齐次化坐标为(wx,wy,w),w为任意不为0的数如果w取值为1,则称为规范化的齐次坐标。
二维几何变换矩阵
由于二维点的齐次坐标是一个包含三个元素的列向量,那么二维几何变换矩阵就应该是一个3*3的方阵。
T
=
[
a
b
e
c
d
f
p
q
s
]
T=\begin{bmatrix} a&b&e\\ c&d&f\\ p&q&s \end{bmatrix}
T=
acpbdqefs
从功能上可以把二维变换矩阵分成四个子矩阵
T
0
=
[
a
b
c
d
]
T
1
=
[
e
f
]
T
2
=
[
p
q
]
T
3
=
[
s
]
T
0
对图形进行比例,旋转,反射和错切变换
,
T
1
进行平移变化,
T
2
进行投影变换
,
T
3
进行整体比例变换
T_0=\begin{bmatrix} a&b\\ c&d \end{bmatrix} T_1=\begin{bmatrix} e\\f\end{bmatrix} T_2=\begin{bmatrix} p&q\end{bmatrix} T_3=\begin{bmatrix} s\end{bmatrix}\\ T_0对图形进行比例,旋转,反射和错切变换,T_1进行平移变化,T_2进行投影变换,T_3进行整体比例变换
T0=[acbd]T1=[ef]T2=[pq]T3=[s]T0对图形进行比例,旋转,反射和错切变换,T1进行平移变化,T2进行投影变换,T3进行整体比例变换
二维图形基本几何变换矩阵
平移变换
平移变换是图形上每一个点移动相同的坐标,假设有一点
P
(
x
,
y
)
移动到点
P
′
(
x
′
,
y
′
)
P(x,y)移动到点P^{\prime}(x^{\prime},y^{\prime})
P(x,y)移动到点P′(x′,y′)
{
x
′
=
x
+
T
x
y
′
=
y
+
T
y
(
T
x
,
T
y
是平移系数
)
\begin{cases} x^{\prime}=x+T_x\\ y^{\prime}=y+T_y \end{cases}(T_x,T_y是平移系数)
{x′=x+Txy′=y+Ty(Tx,Ty是平移系数)
设二维平移变换矩阵为T
[
x
′
y
′
1
]
=
[
x
+
T
x
y
+
T
y
1
]
=
T
[
x
y
1
]
得到
T
=
[
1
0
T
x
0
1
T
y
0
0
1
]
\begin{bmatrix} x^{\prime}\\y^{\prime}\\1 \end{bmatrix}=\begin{bmatrix}x+T_x\\y+T_y\\1\end{bmatrix}=T\begin{bmatrix}x\\y\\1\end{bmatrix}得到T=\begin{bmatrix}1&0&T_x\\0&1&T_y\\0&0&1\end{bmatrix}
x′y′1
=
x+Txy+Ty1
=T
xy1
得到T=
100010TxTy1
比例变换
P
(
x
,
y
)
相对于原点,沿
x
轴方向缩放
S
x
倍,沿
Y
轴方向缩放
S
y
倍到点
P
′
(
x
′
,
y
′
)
P(x,y)相对于原点,沿x轴方向缩放S_x倍,沿Y轴方向缩放S_y倍到点P_{\prime}(x^{\prime},y^{\prime})
P(x,y)相对于原点,沿x轴方向缩放Sx倍,沿Y轴方向缩放Sy倍到点P′(x′,y′)
设二维变换矩阵为T
[
x
′
y
′
1
]
=
[
x
T
x
y
T
y
1
]
=
T
[
x
y
1
]
得到
T
=
[
T
x
0
0
0
T
y
0
0
0
1
]
\begin{bmatrix} x^{\prime}\\y^{\prime}\\1 \end{bmatrix}=\begin{bmatrix}xT_x\\yT_y\\1\end{bmatrix}=T\begin{bmatrix}x\\y\\1\end{bmatrix}得到T=\begin{bmatrix}T_x&0&0\\0&T_y&0\\0&0&1\end{bmatrix}
x′y′1
=
xTxyTy1
=T
xy1
得到T=
Tx000Ty0001
更一般的情况,
P
相对于点
(
p
x
,
p
y
)
进行比例变换到
P
′
P相对于点(p_x,p_y)进行比例变换到P^{\prime}
P相对于点(px,py)进行比例变换到P′。有
{
x
′
=
S
x
(
x
−
p
x
)
+
p
x
y
′
=
S
y
(
y
−
p
y
)
+
p
y
\begin{cases} x^{\prime}=S_x(x-p_x)+p_x\\ y^{\prime}=S_y(y-p_y)+p_y \end{cases}
{x′=Sx(x−px)+pxy′=Sy(y−py)+py
此时的二维变换矩阵T为
T
=
[
S
x
0
p
x
(
1
−
S
x
)
0
S
y
p
y
(
1
−
S
y
)
0
0
1
]
\\T=\begin{bmatrix} S_x&0&p_x(1-S_x)\\ 0&S_y&p_y(1-S_y)\\ 0&0&1 \end{bmatrix}
T=
Sx000Sy0px(1−Sx)py(1−Sy)1
旋转变换
点P相对于坐标原点旋转一个角度
β
\beta
β得到点
P
′
P^{\prime}
P′。
对点P,用极坐标可以表示成
{
x
=
r
cos
α
y
=
r
sin
α
\begin{cases} x=r \cos\alpha\\y=r \sin\alpha \end{cases}
{x=rcosαy=rsinα
旋转后点
P
′
P^{\prime}
P′,用极坐标表示为
{
x
′
=
r
cos
(
α
+
β
)
=
r
cos
α
cos
β
−
r
sin
α
sin
β
=
x
cos
β
−
y
sin
β
y
′
=
r
sin
(
α
+
β
)
=
r
sin
α
cos
β
+
r
cos
α
sin
β
=
y
cos
β
+
x
sin
β
\begin{cases} x^{\prime}=r\cos(\alpha+\beta)=r\cos\alpha\cos\beta-r\sin\alpha\sin\beta=x\cos\beta-y\sin\beta \\y^{\prime}=r\sin(\alpha+\beta)=r\sin\alpha\cos\beta+r\cos\alpha\sin\beta=y\cos\beta+x\sin\beta \end{cases}
{x′=rcos(α+β)=rcosαcosβ−rsinαsinβ=xcosβ−ysinβy′=rsin(α+β)=rsinαcosβ+rcosαsinβ=ycosβ+xsinβ
由此可以得到二维变换矩阵T
T
=
[
cos
β
−
sin
β
0
sin
β
cos
β
0
0
0
1
]
T=\begin{bmatrix} \cos\beta&-\sin\beta&0\\ \sin\beta&\cos\beta&0\\ 0&0&1 \end{bmatrix}
T=
cosβsinβ0−sinβcosβ0001
更一般的情况,点P绕点
(
p
x
,
p
y
)
(p_x,p_y)
(px,py)旋转角度
β
\beta
β
{
x
′
=
(
x
−
p
x
)
cos
β
−
(
y
−
p
y
)
sin
β
+
p
x
y
′
=
(
x
−
p
x
)
sin
β
+
(
y
−
p
y
)
cos
β
+
p
y
\begin{cases} x^{\prime}=(x-p_x)\cos\beta-(y-p_y)\sin\beta+p_x\\ y^{\prime}=(x-p_x)\sin\beta+(y-p_y)\cos\beta+p_y \end{cases}
{x′=(x−px)cosβ−(y−py)sinβ+pxy′=(x−px)sinβ+(y−py)cosβ+py
此时的二维变换矩阵T为
T
=
[
cos
β
−
sin
β
p
x
(
1
−
cos
β
)
+
p
y
sin
β
sin
β
cos
β
p
y
(
1
−
cos
β
)
−
p
x
sin
β
0
0
1
]
T=\begin{bmatrix} \cos\beta&-\sin\beta&p_x(1-\cos\beta)+p_y\sin\beta\\ \sin\beta&\cos\beta&p_y(1-\cos\beta)-p_x\sin\beta\\ 0&0&1 \end{bmatrix}
T=
cosβsinβ0−sinβcosβ0px(1−cosβ)+pysinβpy(1−cosβ)−pxsinβ1
对称变换
关于原点对称,二维变换矩阵T
T
=
[
−
1
0
0
0
−
1
0
0
0
1
]
T=\begin{bmatrix} -1&0&0\\ 0&-1&0\\ 0&0&1 \end{bmatrix}
T=
−1000−10001
关于x轴对称,二维变换矩阵T
T
=
[
1
0
0
0
−
1
0
0
0
1
]
T=\begin{bmatrix} 1&0&0\\ 0&-1&0\\ 0&0&1 \end{bmatrix}
T=
1000−10001
关于轴对称,二维变换矩阵T
T
=
[
−
1
0
0
0
1
0
0
0
1
]
T=\begin{bmatrix} -1&0&0\\ 0&1&0\\ 0&0&1 \end{bmatrix}
T=
−100010001
关于点
(
p
x
,
p
y
)
(p_x,p_y)
(px,py)对称的二维变换矩阵T
T
=
[
−
1
0
2
p
x
0
−
1
2
p
y
0
0
1
]
T=\begin{bmatrix} -1&0&2p_x\\ 0&-1&2p_y\\ 0&0&1 \end{bmatrix}
T=
−1000−102px2py1
错切变换
错切是点P相对于x,y轴进行不等量变化的过程。
相对于原点进行错切变换
{
x
′
=
x
+
λ
x
y
y
′
=
λ
y
x
+
y
\begin{cases} x^{\prime}=x+\lambda _x y\\y^{\prime}=\lambda _yx+y \end{cases}
{x′=x+λxyy′=λyx+y
二维变换矩阵T为
T
=
[
1
λ
x
0
λ
y
1
0
0
0
1
]
T=\begin{bmatrix} 1&\lambda _x&0\\ \lambda _y&1&0\\ 0&0&1 \end{bmatrix}
T=
1λy0λx10001
相对于点
(
p
x
,
p
y
)
(p_x,p_y)
(px,py)进行错切变换
{
x
′
=
x
+
λ
x
(
y
−
p
x
)
y
′
=
λ
y
(
x
−
p
y
)
+
y
\begin{cases} x^{\prime}=x+\lambda _x (y-p_x)\\y^{\prime}=\lambda _y(x-p_y)+y \end{cases}
{x′=x+λx(y−px)y′=λy(x−py)+y
二维变换矩阵T为
T
=
[
1
λ
x
−
λ
x
p
x
λ
y
1
−
λ
y
p
y
0
0
1
]
T=\begin{bmatrix} 1&\lambda _x&-\lambda _xp_x\\ \lambda _y&1&-\lambda _yp_y\\ 0&0&1 \end{bmatrix}
T=
1λy0λx10−λxpx−λypy1
代码实现
二维点类
class CPoint2
{
public:
CPoint2();
CPoint2(double x, double y);
~CPoint2();
friend CPoint2 operator +(const CPoint2& p0, const CPoint2& p1);
friend CPoint2 operator -(const CPoint2& p0, const CPoint2& p1);
friend CPoint2 operator *(const CPoint2& p0, double t);
friend CPoint2 operator *(double t, const CPoint2& p0);
friend CPoint2 operator /(const CPoint2& p0, double t);
friend CPoint2 operator +=(const CPoint2& p0, const CPoint2& p1);
friend CPoint2 operator -=(const CPoint2& p0, const CPoint2& p1);
friend CPoint2 operator *=(const CPoint2& p0, const CPoint2& p1);
friend CPoint2 operator /=(const CPoint2& p0, const CPoint2& p1);
public:
double m_x, m_y, m_w;
};
CPoint2::CPoint2()
{
m_x = 0;
m_y = 0;
m_w = 1;
}
CPoint2::CPoint2(double x, double y)
{
m_x = x;
m_y = y;
m_w = 1;
}
CPoint2::~CPoint2()
{
}
CPoint2 operator+(const CPoint2& p0, const CPoint2& p1)
{
CPoint2 res;
res.m_x = p0.m_x + p1.m_x;
res.m_y = p0.m_y + p1.m_y;
return res;
}
CPoint2 operator-(const CPoint2& p0, const CPoint2& p1)
{
CPoint2 res;
res.m_x = p0.m_x - p1.m_x;
res.m_y = p0.m_y - p0.m_y;
return res;
}
CPoint2 operator*(const CPoint2& p0, double t)
{
CPoint2 res;
res.m_x = p0.m_x * t;
res.m_y = p0.m_y + t;
return res;
}
CPoint2 operator*(double t, const CPoint2& p0)
{
CPoint2 res;
res.m_x = p0.m_x * t;
res.m_y = p0.m_y * t;
return res;
}
CPoint2 operator/(const CPoint2& p0, double t)
{
if (fabs(t) < 1e-4)
t = 1.0;
CPoint2 res;
res.m_x = p0.m_x / t;
res.m_y = p0.m_y / t;
return res;
}
CPoint2 operator+=(CPoint2& p0, CPoint2& p1)
{
p0.m_x += p1.m_x;
p0.m_y += p1.m_y;
return p0;
}
CPoint2 operator-=(CPoint2& p0,CPoint2& p1)
{
p0.m_x -= p1.m_x;
p0.m_y -= p1.m_y;
return p0;
}
CPoint2 operator*=(CPoint2& p0, CPoint2& p1)
{
p0.m_x *= p1.m_x;
p0.m_y *= p1.m_y;
return p0;
}
CPoint2 operator/=(CPoint2& p0,CPoint2& p1)
{
if (fabs(p1.m_x) < 1e-4||fabs(p1.m_y)<1e-4)
p1.m_x = 1.0,p1.m_y=1.0;
p0.m_x /= p1.m_x;
p0.m_y /= p1.m_y;
return p0;
}
二维变换类
class CTransform2
{
public:
CTransform2();
~CTransform2();
void SetMatrix(CPoint2* p, int pNum);//设置矩阵
void IdentityMatrix();//单位矩阵
void Translate(double tx, double ty);
void Translate(double tx, double ty, CPoint2 p);
void Scale(double sx, double sy);
void Scale(double sx, double sy, CPoint2 p);
void Rotate(double beta);
void Rotate(double beta, CPoint2 p);
void SymmetryO();
void SymmetryX();
void SymmetryY();
void Shear(double lambdax, double lambday);
void Shear(double lambdax, double lambday,CPoint2 p);
void MatrixMultiply();
public:
double m_matrix[3][3];
CPoint2* m_p;
int m_pNum;
};
CTransform2::CTransform2()
{
}
CTransform2::~CTransform2()
{
}
void CTransform2::SetMatrix(CPoint2* p, int pNum)
{
m_p = p;
m_pNum = pNum;
}
void CTransform2::IdentityMatrix()
{
m_matrix[0][0] = 1.0; m_matrix[0][0] = 0.0; m_matrix[0][0] = 0.0;
m_matrix[0][0] = 0.0; m_matrix[0][0] = 1.0; m_matrix[0][0] = 0.0;
m_matrix[0][0] = 0.0; m_matrix[0][0] = 0.0; m_matrix[0][0] = 1.0;
}
void CTransform2::Translate(double tx, double ty)
{
IdentityMatrix();
m_matrix[0][2] = tx;
m_matrix[1][2] = ty;
MatrixMultiply();
}
void CTransform2::Translate(double tx, double ty, CPoint2 p)
{
}
void CTransform2::Scale(double sx, double sy)
{
IdentityMatrix();
m_matrix[0][0] = sx;
m_matrix[1][1] = sy;
MatrixMultiply();
}
void CTransform2::Scale(double sx, double sy, CPoint2 p)
{
IdentityMatrix();
m_matrix[0][0] = sx;
m_matrix[0][0] = p.m_x*(1-sx);
m_matrix[1][1] = sy;
m_matrix[1][2] = p.m_y*(1-sy);
MatrixMultiply();
}
void CTransform2::Rotate(double beta)
{
IdentityMatrix();
m_matrix[0][0] = cos(beta * PI / 180);
m_matrix[0][1] = -sin(beta * PI / 180);
m_matrix[1][0] = sin(beta * PI / 180);
m_matrix[1][1] = cos(beta * PI / 180);
MatrixMultiply();
}
void CTransform2::Rotate(double beta, CPoint2 p)
{
IdentityMatrix();
m_matrix[0][0] = cos(beta * PI / 180);
m_matrix[0][1] = -sin(beta * PI / 180);
m_matrix[0][2] = p.m_x * (1 - cos(beta)) + p.m_y * sin(beta);
m_matrix[1][0] = sin(beta * PI / 180);
m_matrix[1][1] = cos(beta * PI / 180);
m_matrix[1][2] = p.m_y * (1 - cos(beta)) + p.m_y * sin(beta);
MatrixMultiply();
}
void CTransform2::SymmetryO()
{
IdentityMatrix();
m_matrix[0][0] = -1;
m_matrix[1][1] = -1;
MatrixMultiply();
}
void CTransform2::SymmetryX()
{
IdentityMatrix();
m_matrix[1][1] = -1;
MatrixMultiply();
}
void CTransform2::SymmetryY()
{
IdentityMatrix();
m_matrix[0][0] = -1;
MatrixMultiply();
}
void CTransform2::Shear(double lambdax, double lambday)
{
IdentityMatrix();
m_matrix[0][1] = lambdax;
m_matrix[1][0] = lambday;
MatrixMultiply();
}
void CTransform2::Shear(double lambdax, double lambday, CPoint2 p)
{
IdentityMatrix();
m_matrix[0][1] = lambdax;
m_matrix[0][2] = -p.m_x * lambdax;
m_matrix[1][0] = lambday;
m_matrix[1][2] = -p.m_y * lambday;
MatrixMultiply();
}
void CTransform2::MatrixMultiply()
{
CPoint2 *pTemp = new CPoint2[m_pNum];
for (int i = 0; i < m_pNum; i++) {
pTemp[i]=m_p[i];
}
for (int i = 0; i < m_pNum; i++) {
m_p[i].m_x = m_matrix[0][0] * pTemp[i].m_x
+ m_matrix[0][1] * pTemp[i].m_y + m_matrix[0][2] * pTemp[i].m_w;
m_p[i].m_y = m_matrix[1][0] * pTemp[i].m_x
+ m_matrix[1][1] * pTemp[i].m_y + m_matrix[1][2] * pTemp[i].m_w;
m_p[i].m_w = m_matrix[2][0] * pTemp[i].m_x
+ m_matrix[2][1] * pTemp[i].m_y + m_matrix[2][2] * pTemp[i].m_w;
}
delete[] pTemp;
}
基本图元
class CBasic
{
public:
CBasic();
~CBasic();
void Draw(CDC* pDC);
public:
CPoint2 m_p[12];
};
CBasic::CBasic()
{
m_p[0] = CPoint2(20, 20);
m_p[1] = CPoint2(-20, 20);
m_p[2] = CPoint2(-20, -20);
m_p[3] = CPoint2(20, -20);
m_p[4] = CPoint2(50, 50);
m_p[5] = CPoint2(-50, 50);
m_p[6] = CPoint2(-50, -50);
m_p[7] = CPoint2(50, -50);
m_p[8] = CPoint2((m_p[4].m_x + m_p[7].m_x) / 2 + 40, (m_p[4].m_y + m_p[7].m_y) / 2);
m_p[9] = CPoint2((m_p[4].m_x + m_p[5].m_x) / 2, (m_p[4].m_y + m_p[5].m_y) / 2 + 40);
m_p[10] = CPoint2((m_p[5].m_x + m_p[6].m_x) / 2 - 40, (m_p[5].m_y + m_p[6].m_y) / 2);
m_p[11] = CPoint2((m_p[6].m_x + m_p[7].m_x) / 2, (m_p[6].m_y + m_p[7].m_y) / 2 - 40);
}
CBasic::~CBasic()
{
}
void CBasic::Draw(CDC* pDC)
{
CPen NewPen(PS_SOLID, 3, RGB(0, 0, 0));
auto OldPen = pDC->SelectObject(&NewPen);
pDC->MoveTo(m_p[0].m_x, m_p[0].m_y);
pDC->LineTo(m_p[1].m_x, m_p[1].m_y);
pDC->LineTo(m_p[2].m_x, m_p[2].m_y);
NewPen.DeleteObject();
NewPen.CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
OldPen = pDC->SelectObject(&NewPen);
pDC->LineTo(m_p[3].m_x, m_p[3].m_y);
pDC->LineTo(m_p[0].m_x, m_p[0].m_y);
NewPen.DeleteObject();
NewPen.CreatePen(PS_SOLID, 3, RGB(0,255, 0));
OldPen = pDC->SelectObject(&NewPen);
pDC->MoveTo(m_p[4].m_x, m_p[4].m_y);
pDC->LineTo(m_p[5].m_x, m_p[5].m_y);
pDC->LineTo(m_p[6].m_x, m_p[6].m_y);
NewPen.DeleteObject();
NewPen.CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
OldPen = pDC->SelectObject(&NewPen);
pDC->LineTo(m_p[7].m_x, m_p[7].m_y);
pDC->LineTo(m_p[4].m_x, m_p[4].m_y);
NewPen.DeleteObject();
NewPen.CreatePen(PS_SOLID, 3, RGB(0, 255, 255));
OldPen = pDC->SelectObject(&NewPen);
pDC->MoveTo(m_p[8].m_x, m_p[8].m_y);
pDC->LineTo(m_p[10].m_x, m_p[10].m_y);
NewPen.DeleteObject();
NewPen.CreatePen(PS_SOLID, 3, RGB(255, 0, 255));
OldPen = pDC->SelectObject(&NewPen);
pDC->MoveTo(m_p[9].m_x, m_p[9].m_y);
pDC->LineTo(m_p[11].m_x, m_p[11].m_y);
pDC->SelectObject(OldPen);
}
测试效果
//平移
//缩放
//平移+旋转
//平移+错切 lambdax=0.5,lambday=0.5
//对称+平移