《计算机图形学》 第一章 基础知识--02向量(二维)

  前沿:在计算机图形学中,我们会用到大量的数学知识,所以今天我们来看看第一个数学知识点:二维向量

一、二维向量知识点:

  

 

二、代码实现

  1.Vector2

  

#ifndef _VECTOR_2_H_
#define _VECTOR_2_H_

#include <cmath>
#include <cassert>

#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 <windows.h>
#include <time.h>
#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;
}

 

posted @ 2017-11-04 15:35  Dean二十七  阅读(912)  评论(0编辑  收藏  举报