09-平面着色的三角形填充算法

参考:https://www.bilibili.com/video/BV1Kr4y1Q7kq?p=9

代码

// 09-平面着色的三角形填充算法
// 参考 https://www.bilibili.com/video/BV1Kr4y1Q7kq?p=9
#ifndef UNICODE
#define UNICODE
#endif 
#include <math.h>
#include <Windows.h>
#include <stdio.h>
#include <wchar.h>
#define PI 3.1415926         // PI 
#define ROUND(d) int(d+0.5)  // 四舍五入
#define WINDOW_TEXT L"09-平面着色的三角形填充算法"

// 二维点
struct Point2
{
    Point2() :x(0), y(0) {}
    Point2(double x, double y) :x(x), y(y) {}
    double  x;
    double  y;
};

// 填充三角形
class Triangle
{
#define LEFT  true
#define RIGHT false
public:
    Triangle() {};
    Triangle(Point2 p0, Point2 p1 , Point2 p2)
    {
        P[0] = p0;
        P[1] = p1;
        P[2] = p2;
    }
    ~Triangle() {};

    void Fill(HDC hdc)
    {
        // 顶点按照y坐标 由小到大排序
        SortPoint();

        int nTotalScanLine = P[2].y - P[0].y + 1;   // 扫描线数量

        // 定义span起点与终点数字
        _pSpanLeft  = new Point2[nTotalScanLine];
        _pSpanRight = new Point2[nTotalScanLine];

        // 判断三角形与P0 P1的位置关系,0-1-2 左手系
        int nDeltz = (P[2].x - P[0].x) * (P[1].y - P[0].y) - (P[2].y - P[0].y) * (P[1].x - P[0].x); // 叉积

       
        if (nDeltz > 0)   // 左三角形
        {
            _nIndex = 0;
            EdgeFlag(P[0], P[1], LEFT);
            EdgeFlag(P[1], P[2], LEFT);
            _nIndex = 0;
            EdgeFlag(P[0], P[2], RIGHT);
        }
        else // 右三角形
        {
            _nIndex = 0;
            EdgeFlag(P[0], P[2], LEFT);
            _nIndex = 0;
            EdgeFlag(P[0], P[1], RIGHT);
            EdgeFlag(P[1], P[2], RIGHT);
        }

        for (int y = P[0].y; y < P[2].y; y++)
        {
            int n = y - P[0].y;

            MoveToEx(hdc,_pSpanLeft[n].x,y,NULL);
            LineTo(hdc, _pSpanRight[n].x,y);

        }

        if (_pSpanLeft)
        {
            delete[] _pSpanLeft;
            _pSpanLeft = NULL;
        }

        if (_pSpanRight)
        {
            delete[] _pSpanRight;
            _pSpanRight = NULL;
        }
    }

private :

    // 边标记算法
    void EdgeFlag(Point2 pStart, Point2 pEnd, bool bFeature)
    {
        int dx = pEnd.x - pStart.x;
        int dy = pEnd.y - pStart.y;

        double m = double(dx) / dy;

        double x = pStart.x;

        for (int y = pStart.y; y < pEnd.y; y++)
        {
            if (bFeature)
            {
                _pSpanLeft[_nIndex++] = Point2(ROUND(x), y);

            }
            else
            {
                _pSpanRight[_nIndex++] = Point2(ROUND(x), y);
            }

            x += m;
        }
       
    }

    // 顶点排序
    void SortPoint()
    {
        Point2 pt;  // 排序后 P[0].y < P[1].y < P[2].y 

        if (P[0].y>P[1].y)
        {
            pt = P[0];
            P[0] = P[1];
            P[1] = pt;
        }

        if (P[0].y > P[2].y)
        {
            pt = P[0];
            P[0] = P[2];
            P[2] = pt;
        }

        if (P[1].y > P[2].y)
        {
            pt = P[1];
            P[1] = P[2];
            P[2] = pt;
        }
    }
private:
    Point2  P[3];       // 顶点
    Point2* _pSpanLeft;  // 跨度起点
    Point2* _pSpanRight; // 跨度终点
    int     _nIndex;    // 扫描线索引

};

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc;
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        hdc = BeginPaint(hwnd, &ps);

        HGDIOBJ original = NULL;
        original = SelectObject(hdc, GetStockObject(DC_PEN));
        SetDCPenColor(hdc, RGB(0, 0, 100));

        GetClientRect(hwnd, &rc);
        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
        SetViewportExtEx(hdc, rc.right, -rc.bottom, NULL);
        SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);

        {
            Point2 p1(   0,   250);
            Point2 p2(-400,  -250);
            Point2 p3( 400,  -250);

            Triangle trangile(p1, p2, p3);
            trangile.Fill(hdc);
        }  

        SelectObject(hdc, original);
        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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值