参考:https://www.bilibili.com/video/BV12W4y1v75
原始图形
// 窗格坐标点
_P[ 0] = Point2(-4,-4);
_P[ 1] = Point2(-2,-4);
_P[ 2] = Point2( 0,-4);
_P[ 3] = Point2( 2,-4);
_P[ 4] = Point2( 4,-4);
_P[ 5] = Point2(-2,-3);
_P[ 6] = Point2( 2,-3);
_P[ 7] = Point2(-4,-2);
_P[ 8] = Point2(-3,-2);
_P[ 9] = Point2( 3,-2);
_P[10] = Point2( 4,-2);
_P[11] = Point2(-1.5, -1.5);
_P[12] = Point2(0, -1.5);
_P[13] = Point2(1.5, -1.5);
_P[14] = Point2(-4, 0);
_P[15] = Point2(-1.5, 0);
_P[16] = Point2(1.5, 0);
_P[17] = Point2( 4, 0);
_P[18] = Point2(-1.5, 1.5);
_P[19] = Point2(0, 1.5);
_P[20] = Point2(1.5, 1.5);
_P[21] = Point2(-4, 2);
_P[22] = Point2(-3, 2);
_P[23] = Point2(3, 2);
_P[24] = Point2(4,2);
_P[25] = Point2(-2, 3);
_P[26] = Point2(2, 3);
_P[27] = Point2(-4, 4);
_P[28] = Point2(-2, 4);
_P[29] = Point2(0, 4);
_P[30] = Point2(2, 4);
_P[31] = Point2(4, 4);
平移变换
比例变换
旋转变换
反射变换
错切变换
复合变换
矩阵操作
class Transform2
{
private:
double _T[3][3]; // 变换矩阵
Point2* _P; // 点
int _ptNumber; // 顶点个数
public:
void SetMatrix(Point2* p, int ptNumber) // 设置二维顶点数组
{
this->_P = p;
this->_ptNumber = ptNumber;
}
void Identity() // 单位矩阵
{
_T[0][0] = 1.0; _T[0][1] = 0.0; _T[0][2] = 0.0;
_T[1][0] = 0.0; _T[1][1] = 1.0; _T[1][2] = 0.0;
_T[2][0] = 0.0; _T[2][1] = 0.0; _T[2][2] = 1.0;
}
void Translate(double tx,double ty) // 平移
{
Identity();
_T[2][0] = tx;
_T[2][1] = ty;
MultiplyMatrix();
}
void Scale(double sx,double sy) // 缩放
{
Identity();
_T[0][0] = sx;
_T[1][1] = sy;
MultiplyMatrix();
}
void Scale(double sx, double sy,Point2 p) // 相对于点缩放
{
Translate(-p.x,-p.y); // 移至坐标原点
Scale(sx, sy);
Translate(p.x,p.y); // 移回
}
void Rotate(double beta) //旋转
{
Identity();
double rad = beta * PI / 180;
_T[0][0] = cos(rad); _T[0][1] = sin(rad);
_T[1][0] = -sin(rad); _T[1][1] = cos(rad);
MultiplyMatrix();
}
void Rotate(double beta , Point2 p) //相对于点旋转
{
Translate(-p.x, -p.y);
Rotate(beta);
Translate(p.x, p.y);
}
void ReflectOry() // 原点反射
{
Identity();
_T[0][0] = -1;
_T[1][1] = -1;
MultiplyMatrix();
}
void ReflectX() // X轴反射
{
Identity();
_T[0][0] = 1;
_T[1][1] = -1;
MultiplyMatrix();
}
void ReflectY() // Y轴反射
{
Identity();
_T[0][0] = -1;
_T[1][1] = 1;
MultiplyMatrix();
}
void Shear(double b,double c) // 错切
{
Identity();
_T[0][0] = b;
_T[1][1] = c;
MultiplyMatrix();
}
void MultiplyMatrix() // 矩阵相乘
{
Point2* pTemp = new Point2[_ptNumber];
for (int i = 0; i < _ptNumber; i++)
{
pTemp[i] = _P[i];
}
for (int i = 0; i < _ptNumber; i++)
{
_P[i].x = pTemp[i].x * _T[0][0] + pTemp[i].y * _T[1][0] + pTemp[i].w * _T[2][0];
_P[i].y = pTemp[i].x * _T[0][1] + pTemp[i].y * _T[1][1] + pTemp[i].w * _T[2][1];
_P[i].w = pTemp[i].x * _T[0][2] + pTemp[i].y * _T[1][2] + pTemp[i].w * _T[2][2];
}
delete[] pTemp;
}
};
完整代码
// 16-二维图形几何变换算法
// 参考 https://www.bilibili.com/video/BV12W4y1v75a
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"16-二维图形几何变换算法"
#define ROUND(d) int(d+0.5) // 四舍五入
#define PI 3.1415926
struct Point2 // 二维点
{
double x;
double y;
double w;
Point2() :x(0), y(0), w(0) {}
Point2(double x, double y) :x(x), y(y), w(0) {}
Point2(double x, double y,double w) :x(x), y(y), w(w) {}
};
class Transform2
{
private:
double _T[3][3]; // 变换矩阵
Point2* _P; // 点
int _ptNumber; // 顶点个数
public:
void SetMatrix(Point2* p, int ptNumber) // 设置二维顶点数组
{
this->_P = p;
this->_ptNumber = ptNumber;
}
void Identity() // 单位矩阵
{
_T[0][0] = 1.0; _T[0][1] = 0.0; _T[0][2] = 0.0;
_T[1][0] = 0.0; _T[1][1] = 1.0; _T[1][2] = 0.0;
_T[2][0] = 0.0; _T[2][1] = 0.0; _T[2][2] = 1.0;
}
void Translate(double tx,double ty) // 平移
{
Identity();
_T[2][0] = tx;
_T[2][1] = ty;
MultiplyMatrix();
}
void Scale(double sx,double sy) // 缩放
{
Identity();
_T[0][0] = sx;
_T[1][1] = sy;
MultiplyMatrix();
}
void Scale(double sx, double sy,Point2 p) // 相对于点缩放
{
Translate(-p.x,-p.y); // 移至坐标原点
Scale(sx, sy);
Translate(p.x,p.y); // 移回
}
void Rotate(double beta) //旋转
{
Identity();
double rad = beta * PI / 180;
_T[0][0] = cos(rad); _T[0][1] = sin(rad);
_T[1][0] = -sin(rad); _T[1][1] = cos(rad);
MultiplyMatrix();
}
void Rotate(double beta , Point2 p) //相对于点旋转
{
Translate(-p.x, -p.y);
Rotate(beta);
Translate(p.x, p.y);
}
void ReflectOry() // 原点反射
{
Identity();
_T[0][0] = -1;
_T[1][1] = -1;
MultiplyMatrix();
}
void ReflectX() // X轴反射
{
Identity();
_T[0][0] = 1;
_T[1][1] = -1;
MultiplyMatrix();
}
void ReflectY() // Y轴反射
{
Identity();
_T[0][0] = -1;
_T[1][1] = 1;
MultiplyMatrix();
}
void Shear(double b,double c) // 错切
{
Identity();
_T[0][0] = b;
_T[1][1] = c;
MultiplyMatrix();
}
void MultiplyMatrix() // 矩阵相乘
{
Point2* pTemp = new Point2[_ptNumber];
for (int i = 0; i < _ptNumber; i++)
{
pTemp[i] = _P[i];
}
for (int i = 0; i < _ptNumber; i++)
{
_P[i].x = pTemp[i].x * _T[0][0] + pTemp[i].y * _T[1][0] + pTemp[i].w * _T[2][0];
_P[i].y = pTemp[i].x * _T[0][1] + pTemp[i].y * _T[1][1] + pTemp[i].w * _T[2][1];
_P[i].w = pTemp[i].x * _T[0][2] + pTemp[i].y * _T[1][2] + pTemp[i].w * _T[2][2];
}
delete[] pTemp;
}
};
class ClassicaPane
{
public:
Point2 _P[32]; // 窗格顶点
public:
ClassicaPane()
{
_P[ 0] = Point2(-4,-4);
_P[ 1] = Point2(-2,-4);
_P[ 2] = Point2( 0,-4);
_P[ 3] = Point2( 2,-4);
_P[ 4] = Point2( 4,-4);
_P[ 5] = Point2(-2,-3);
_P[ 6] = Point2( 2,-3);
_P[ 7] = Point2(-4,-2);
_P[ 8] = Point2(-3,-2);
_P[ 9] = Point2( 3,-2);
_P[10] = Point2( 4,-2);
_P[11] = Point2(-1.5, -1.5);
_P[12] = Point2(0, -1.5);
_P[13] = Point2(1.5, -1.5);
_P[14] = Point2(-4, 0);
_P[15] = Point2(-1.5, 0);
_P[16] = Point2(1.5, 0);
_P[17] = Point2( 4, 0);
_P[18] = Point2(-1.5, 1.5);
_P[19] = Point2(0, 1.5);
_P[20] = Point2(1.5, 1.5);
_P[21] = Point2(-4, 2);
_P[22] = Point2(-3, 2);
_P[23] = Point2(3, 2);
_P[24] = Point2(4,2);
_P[25] = Point2(-2, 3);
_P[26] = Point2(2, 3);
_P[27] = Point2(-4, 4);
_P[28] = Point2(-2, 4);
_P[29] = Point2(0, 4);
_P[30] = Point2(2, 4);
_P[31] = Point2(4, 4);
};
~ ClassicaPane() {};
void Draw(HDC hdc)
{
MoveToEx(hdc,ROUND(_P[0].x), ROUND(_P[0].y),NULL);
LineTo(hdc, ROUND(_P[4].x), ROUND(_P[4].y));
LineTo(hdc, ROUND(_P[31].x), ROUND(_P[31].y));
LineTo(hdc, ROUND(_P[27].x), ROUND(_P[27].y));
LineTo(hdc, ROUND(_P[0].x), ROUND(_P[0].y));
MoveToEx(hdc, ROUND(_P[5].x), ROUND(_P[5].y), NULL);
LineTo(hdc, ROUND(_P[ 6].x), ROUND(_P[ 6].y));
LineTo(hdc, ROUND(_P[ 9].x), ROUND(_P[ 9].y));
LineTo(hdc, ROUND(_P[23].x), ROUND(_P[23].y));
LineTo(hdc, ROUND(_P[26].x), ROUND(_P[26].y));
LineTo(hdc, ROUND(_P[25].x), ROUND(_P[25].y));
LineTo(hdc, ROUND(_P[22].x), ROUND(_P[22].y));
LineTo(hdc, ROUND(_P[ 8].x), ROUND(_P[ 8].y));
LineTo(hdc, ROUND(_P[ 5].x), ROUND(_P[ 5].y));
MoveToEx(hdc, ROUND(_P[11].x), ROUND(_P[11].y), NULL);
LineTo(hdc, ROUND(_P[13].x), ROUND(_P[13].y));
LineTo(hdc, ROUND(_P[20].x), ROUND(_P[20].y));
LineTo(hdc, ROUND(_P[18].x), ROUND(_P[18].y));
LineTo(hdc, ROUND(_P[11].x), ROUND(_P[11].y));
MoveToEx(hdc, ROUND(_P[7].x), ROUND(_P[7].y), NULL);
LineTo(hdc, ROUND(_P[8].x), ROUND(_P[8].y));
MoveToEx(hdc, ROUND(_P[1].x), ROUND(_P[1].y), NULL);
LineTo(hdc, ROUND(_P[5].x), ROUND(_P[5].y));
MoveToEx(hdc, ROUND(_P[2].x), ROUND(_P[2].y), NULL);
LineTo(hdc, ROUND(_P[12].x), ROUND(_P[12].y));
MoveToEx(hdc, ROUND(_P[3].x), ROUND(_P[3].y), NULL);
LineTo(hdc, ROUND(_P[6].x), ROUND(_P[6].y));
MoveToEx(hdc, ROUND(_P[10].x), ROUND(_P[10].y), NULL);
LineTo(hdc, ROUND(_P[9].x), ROUND(_P[9].y));
MoveToEx(hdc, ROUND(_P[17].x), ROUND(_P[17].y), NULL);
LineTo(hdc, ROUND(_P[16].x), ROUND(_P[16].y));
MoveToEx(hdc, ROUND(_P[14].x), ROUND(_P[14].y), NULL);
LineTo(hdc, ROUND(_P[15].x), ROUND(_P[15].y));
MoveToEx(hdc, ROUND(_P[23].x), ROUND(_P[23].y), NULL);
LineTo(hdc, ROUND(_P[24].x), ROUND(_P[24].y));
MoveToEx(hdc, ROUND(_P[26].x), ROUND(_P[26].y), NULL);
LineTo(hdc, ROUND(_P[30].x), ROUND(_P[30].y));
MoveToEx(hdc, ROUND(_P[19].x), ROUND(_P[19].y), NULL);
LineTo(hdc, ROUND(_P[29].x), ROUND(_P[29].y));
MoveToEx(hdc, ROUND(_P[25].x), ROUND(_P[25].y), NULL);
LineTo(hdc, ROUND(_P[28].x), ROUND(_P[28].y));
MoveToEx(hdc, ROUND(_P[21].x), ROUND(_P[21].y), NULL);
LineTo(hdc, ROUND(_P[22].x), ROUND(_P[22].y));
/*
... ...
*/
}
};
static bool m_Play = false;
static int m_Angle = 0;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
switch (uMsg)
{
case WM_TIMER:
m_Angle++;
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
m_Play = !m_Play;
if (m_Play)
{
// 产生 WM_TIMER 消息
SetTimer(hwnd, 0, 10, NULL);
}
else
{
// 销毁 WM_TIMER 消息
KillTimer(hwnd, 0);
}
return 0;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
// 内存DC
HDC memDC = CreateCompatibleDC(hdc); // 创建兼容DC 画板
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); // 创建画布
HGDIOBJ oldBitmap = SelectObject(memDC, newBitmap); // 将画布选入画板
FillRect(memDC, &rc, (HBRUSH)(COLOR_WINDOW + 1));
DrawText(memDC, L"单击左键旋转图形", wcslen(L"单击左键开始动画"), &rc, NULL);
SetMapMode(memDC, MM_ANISOTROPIC);
SetWindowExtEx(memDC, rc.right, -rc.bottom, NULL);
SetViewportExtEx(memDC, rc.right,-rc.bottom, NULL);
SetViewportOrgEx(memDC, rc.right / 2, rc.bottom / 2, NULL);
// 绘制图形
{
ClassicaPane pane;
Transform2 tranform;
tranform.SetMatrix(pane._P,32);
tranform.Scale(50.0, 50.0);
tranform.Rotate(m_Angle);
pane.Draw(memDC);
}
// 内存dc复制到设备
BitBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, memDC, ROUND(-rc.right / 2), ROUND(-rc.bottom / 2), SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"CAG";
WNDCLASS wc = { };
wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
WINDOW_TEXT, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}