18-Cohen-Sutherland裁剪算法

参考:https://www.bilibili.com/video/BV1PP411n7YU/

编码原理

不满简取或间弃,进行求交判断。

左  右  下  上  顺序 找矩形与直线交点

简取P0 P1

求P点坐标

直线与窗口延长线相交

算法设计

全部源码

// 18-Cohen-Sutherland裁剪算法
// 参考 https://www.bilibili.com/video/BV1PP411n7YU/

#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"18-Cohen-Sutherland裁剪算法"
#define ROUND(d) int(floor(d)+0.5)  // 四舍五入
#define PI 3.1415926

#define LEFT   1 // 0001
#define RIGHT  2 // 0010
#define BOTTOM 4 // 0100
#define TOP    8 // 1000

struct Point2  // 二维点
{
    double       x;
    double       y;   
    unsigned int rc;// 直线编码
    Point2() :x(0), y(0), rc(0) {}
    Point2(double x, double y) :x(x), y(y), rc(0) {}
};

class TrimLine
{
public :     
    int wxl; //窗口左
    int wxr; //窗口右
    int wyt; //窗口上
    int wyb; //窗口下

public:
    TrimLine()
    {
        wxl = -300;
        wxr =  300;
        wyt =  100;
        wyb = -100;
    };
    ~TrimLine() {};
    
    // 引用 & 改变实参的值 
    void Cohen_Sutherland(Point2 &p0,Point2& p1) // 裁剪线段
    {
        Encode(p0);Encode(p1);

        double k = (p1.y - p0.y) / (p1.x - p0.x); // 线段斜率
        Point2 point;  // 直线与边框交点

        while (p0.rc != 0 || p1.rc != 0) // 至少一个端点在窗口之外的情况
        {
            if ((p0.rc & p1.rc) != 0)   // 断点都在窗口之外 简弃
            {
                p0 = p1 = Point2(0, 0);
                return;
            }

            if (0 == p0.rc) //确保p0 位于窗口之外
            {
                Point2  pTemp = p0;
                p0 = p1;
                p1 = pTemp;
            }

            unsigned int outCode = p0.rc;

            // 窗口边界 按照左 右 下 上 剪裁直线
            if (outCode & LEFT) // P0点位于窗口左侧
            {
                point.x = wxl;
                point.y = k * (point.x - p0.x) + p0.y;
            }
            else if (outCode & RIGHT)// P0点位于窗口右侧
            {
                point.x = wxr;
                point.y = k * (point.x - p0.x) + p0.y;
            }
            else if (outCode & BOTTOM)// P0点位于窗口下侧
            {
                point.y = wyb;
                point.x = (point.y - p0.y) / k + p0.x;
            }
            else if (outCode & TOP)// P0点位于窗口上侧
            {
                point.y = wyt;
                point.x = (point.y - p0.y) / k + p0.x;
            }
            Encode(point);
            p0 = point;
        }

    }
    void DrawClipWindow(HDC hdc)
    {
        HPEN newPen = CreatePen(PS_SOLID,3,RGB(0,0,128));
        HGDIOBJ oldPen= SelectObject(hdc, newPen);

        MoveToEx(hdc,wxl,wyb,NULL);
        LineTo(hdc,wxr,wyb);
        LineTo(hdc,wxr,wyt);
        LineTo(hdc,wxl,wyt);
        LineTo(hdc,wxl,wyb);

        
        SelectObject(hdc, oldPen);
        DeletePen(newPen);

    }

    void Encode(Point2& pt) // 编码函数
    {
        pt.rc = 0;
        if (pt.x < wxl)
            pt.rc |= LEFT;
        else if (pt.x > wxr)
            pt.rc |= RIGHT;
        else if (pt.y < wyb)
            pt.rc |= BOTTOM;
        else if (pt.y > wyt)
            pt.rc |= TOP;  
    }

};




static bool     m_Play = false;
static wchar_t* str    = L"左键 - 裁剪 | 恢复";
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_LBUTTONDOWN:
        m_Play = !m_Play;
        InvalidateRect(hwnd, NULL, TRUE);
        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, str, wcslen(str), &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);

        // 绘制图形
        {     
            TrimLine trim;

            trim.DrawClipWindow(memDC);

            Point2 p0(-400, -200);
            Point2 p1( 400, 200);
            if (m_Play)
            {                
                trim.Cohen_Sutherland(p0,p1); // 裁剪直线
            }               

            MoveToEx(memDC,ROUND(p0.x),ROUND(p0.y),NULL);
            LineTo(memDC, ROUND(p1.x), ROUND(p1.y));
        }

        // 内存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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值