【图形学】二维几何变换的一些理论和实现

一些概念

齐次坐标是用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} xy1 = 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} xy1 = 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(xpx)+pxy=Sy(ypy)+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(1Sx)py(1Sy)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β0sinβ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=(xpx)cosβ(ypy)sinβ+pxy=(xpx)sinβ+(ypy)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β0sinβcosβ0px(1cosβ)+pysinβpy(1cosβ)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= 100010001
关于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= 100010001
关于轴对称,二维变换矩阵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= 1000102px2py1

错切变换

错切是点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(ypx)y=λy(xpy)+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
在这里插入图片描述

//对称+平移

在这里插入图片描述

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值