前沿:在计算机图形学中,我们会用到大量的数学知识,所以今天我们来看看第一个数学知识点:二维向量
一、二维向量知识点:
二、代码实现
1.Vector2
#ifndef _VECTOR_2_H_ #define _VECTOR_2_H_ #include#include #define _IS_ZERO(num) ((num)>=-0.0001&&(num)<=0.0001) class CVector2 { public: float x, y; //构造函数 CVector2(float X = 0.0f, float Y = 0.0f) : x(X), y(Y) {} //设置x、y的值 void Set(float X = 0.0f, float Y = 0.0f) { x = X; y = Y; } //得到向量的长度 float Length() const { return sqrt(x * x + y * y); } //得到向量的单位向量 CVector2 Normalize() const { //得到长度 float len = Length(); //断言,长度不能为0 assert(!_IS_ZERO(len)); //返回单位向量 return CVector2(x / len, y / len); } //重载 + 运算 CVector2 operator + (const CVector2& that) const { return CVector2(x + that.x, y + that.y); } //重载 += 运算 CVector2& operator += (const CVector2& that) { x += that.x; y += that.y; return *this; } //重载 - 运算 CVector2 operator - (const CVector2& that) const { return CVector2(x - that.x, y - that.y); } //重载 向量取符号 CVector2 operator - () const { return CVector2(-x, -y); } //重载 -= 运算 CVector2& operator -= (const CVector2& that) { x -= that.x; y -= that.y; return *this; } //重载 * 运算 --- 向量 * 标量 CVector2 operator * (float num) const { return CVector2(x * num, y * num); } //重载 *= 运算 CVector2& operator *= (float num) { x *= num; y *= num; return *this; } }; //如果需要让 标量 * 向量 也重载的话,那么不能写在向量类(CVector2)中 //凡是向量类中的运算符重载,都是默认指定 this 在运算符的左侧,所以我们 //只能去重载全局的乘法 //在函数声明或定义中函数返回类型前加上关键字inline,即可以把函数指定为内联函数。 //关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声明前面不起任何作用。 //inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。 //一般的,用户可以阅读函数的声明,但是看不到函数的定义。 inline CVector2 operator * (float num, const CVector2 v) { return CVector2(v.x * num, v.y * num); } #endif
2.运行
#include#include #include "Vector2.h" #pragma comment(lib, "msimg32.lib") #define _CLIENT_W 640 #define _CLIENT_H 480 #define _SLEEP_TIME 33 BOOL g_Act = TRUE;//窗口激活标志 HDC g_MainDC; HDC g_BackDC; #define _R 32 //半径 #define _SPEED 20.0f //速率 #define _SPEED_SUB 0.1f //不断减小的速度 CVector2 pos; //位置 CVector2 speed; //加速度 void GameInit(HWND hwnd) { g_MainDC = GetDC(hwnd); g_BackDC = CreateCompatibleDC(g_MainDC); HBITMAP hbmp = CreateCompatibleBitmap(g_MainDC, _CLIENT_W, _CLIENT_H); DeleteObject(SelectObject(g_BackDC, hbmp)); DeleteObject(hbmp); } void GameRun(HWND hwnd) { BitBlt(g_BackDC, 0, 0, _CLIENT_W, _CLIENT_H, 0, 0, 0, WHITENESS); Ellipse(g_BackDC, pos.x - _R, pos.y - _R, pos.x + _R, pos.y + _R); BitBlt(g_MainDC, 0, 0, _CLIENT_W, _CLIENT_H, g_BackDC, 0, 0, SRCCOPY); //如果小球的速度不为0 if (!_IS_ZERO(speed.Length())) { //小球位置移动 pos += speed; //得到加速度大小 float speed_len = speed.Length(); //得到速度单位方向 CVector2 speed_nor = speed.Normalize(); //加速度不断减小 speed_len -= _SPEED_SUB; //如果加速度 <= 0.0f if (speed_len <= 0.0f) { //重新设置加速度 speed.Set(); } //如果加速度 > 0.0f else { //加速度 = 加速度大小 * 加速度单位向量 speed = speed_len * speed_nor; //标量*向量 } } //如果按下鼠标左键 if (GetAsyncKeyState(VK_LBUTTON) & 1) { //记录当前按下的点的位置 POINT p; GetCursorPos(&p); ScreenToClient(hwnd, &p); //如果当前点x >= 0 并且 x < 窗口的左边 并且 当前点y >= 0 并且 y < 窗口呀的下方 if (p.x >= 0 && p.x < _CLIENT_W && p.y >= 0 && p.y < _CLIENT_H) { //位移方向 = 当前点 - 圆圈的当前位置 CVector2 dir = CVector2(p.x, p.y) - pos; //得到加速度 speed = dir.Normalize(); speed *= _SPEED; } } } void GameEnd(HWND hwnd) { DeleteDC(g_BackDC); ReleaseDC(hwnd, g_MainDC); } // 窗口消息函数,本函数将被操作系统调用 __w64 long __stdcall WindowProc(HWND hwnd,//产生消息的窗口 unsigned int uMsg,//消息类型 __w64 unsigned int wParam,//消息附加参数1 __w64 long lParam)//消息附加参数2 { switch (uMsg) { case WM_DESTROY: { PostQuitMessage(0); return 0; } case WM_ACTIVATEAPP: { g_Act = (BOOL)wParam; return 0; } } //我们不关心的消息就调用DefWindowProc(windows对所有消息的默认处理函数)来帮助我们处理 return DefWindowProc(hwnd, uMsg, wParam, lParam); } int __stdcall WinMain(HINSTANCE hInstance,//应用程序实例句柄 HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //01)填充一个窗口类别的结构体 WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = 0; wc.lpszClassName = "3Dtest"; RegisterClass(&wc); int sw = GetSystemMetrics(SM_CXSCREEN); int sh = GetSystemMetrics(SM_CYSCREEN); RECT r = { (sw - _CLIENT_W) / 2, (sh - _CLIENT_H) / 2, (sw - _CLIENT_W) / 2 + _CLIENT_W, (sh - _CLIENT_H) / 2 + _CLIENT_H }; //得到窗口风格 //已知字节????????和风格00001000 //那么(字节&~风格)就为 //???????? //11110111 //-------- //????0??? int ws = (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME) & ~WS_MAXIMIZEBOX; AdjustWindowRect(&r, ws, FALSE); HWND hWnd = CreateWindow( wc.lpszClassName, "向量测试1",//窗口标题栏文字 ws,//窗口风格 r.left,//窗口的左上角x坐标 r.top,//窗口的左上角y坐标 r.right - r.left,//窗口的宽(像素) r.bottom - r.top,//窗口的高(像素) HWND_DESKTOP,//父窗口窗口句柄,HWND_DESKTOP表示桌面 0,//窗口菜单句柄,不使用菜单填0 wc.hInstance,//应用程序实例句柄 0);//任意地址,该地址可以通过WM_CREATE消息得到,不使用设置为0 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); GameInit(hWnd); MSG msg = {}; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else if (g_Act) { int bt = GetTickCount(); GameRun(hWnd); int at = GetTickCount() - bt; Sleep(at < _SLEEP_TIME ? _SLEEP_TIME - at : 1); } else WaitMessage(); } GameEnd(hWnd); return 1; }