20-Liang-Barsky算法

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

算法原理 - 可见侧与不可见侧

算法原理 - 直线参数方程

算法原理 – 裁剪条件

算法原理 – 直线段与窗口边界不平行

算法原理 – 直线段与窗口边界平行-垂直线

算法原理 – 直线段与窗口边界平行-水平线

算法设计

原文案例

源码

// 20-Liang-Barsky算法
// 参考 https://www.bilibili.com/video/BV1AP411n7xb
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"20-Liang-Barsky算法"
#define ROUND(d) int(floor(d)+0.5)  // 四舍五入


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

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

public:
    ClipLine()
    {
        wxl = -300;
        wxr = 300;
        wyt = 100;
        wyb = -100;
    };
    ~ClipLine() {};

    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 LiangBarsky(Point2& P0, Point2& P1)   // Liang-Barsky 裁剪算法
    {
        double x0 = P0.x;
        double y0 = P0.y;
        double x1 = P1.x;
        double y1 = P1.y;
        double t0 = 0.0;
        double t1 = 1.0;

        double deltaX = P1.x - P0.x;
        if (Clipt(-deltaX, x0 - wxl, t0, t1))             // i=1 左边界 p1 = -deltaX, q1 = x0 - wxl
        {                                                    
            if (Clipt(deltaX, wxr - x0, t0, t1))          // i=2 右边界 p2 =  deltaX, q2 = wxr - x0
            {                                                
                double deltaY = P1.y - P0.y;                 
                if (Clipt(-deltaY, y0 - wyb, t0, t1))     // i=3 下边界 p3 = -deltaY, q3 = y0 - wyb
                {                                            
                    if (Clipt(deltaY, wyt - y0, t0, t1))  // i=4 上边界 p4 =  deltaY, q4 = wyt - y0
                    {
                        if (t1 < 1.0) // 计算裁剪后的直线段的终点
                        {
                            P1.x = x0 + t1 * deltaX;     // 重新计算直线段的终点坐标
                            P1.y = y0 + t1 * deltaY;     // x=x0+t(x1-x0)

                        }
                        if (t1 > 0.0)// 计算裁剪后的直线段的起点
                        {
                            P0.x = x0 + t0 * deltaX;     // 重新计算直线段的终点坐标
                            P0.y = y0 + t0 * deltaY;     // x=x0+t(x1-x0)
                        }

                    }
                }
            }

        }
    }

    bool Clipt(double p,double q,double&t0,double &t1) // 裁剪测试
    {
        double r;
        bool bAccept = true;     // 接受
        if (p < 0)               // 直线段从窗口边界的不可见侧到可见侧,计算起点处的t0
        {
            r = q / p;
            if (r > t1)
                bAccept = false; // 拒绝
            else if (r > t0)
                t0 = r;
        }
        else if (p > 0)          // 直线段从窗口边界的可见侧到不可见侧,计算终点处的t1
        {
            r = q / p;
            if (r < t0)
                bAccept = false; // 拒绝
            else if (r < t1)
                t1 = r;
        }
        else                     // 平行窗口边界的直线,p=0
        {
            if (q < 0)           // 直线段在窗口外可直接删除
            {
                bAccept = false; // 拒绝
            }
        }
        return bAccept;
    }
};

static bool     m_Play = false;
static wchar_t* m_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, m_str, wcslen(m_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);

        // 绘制图形
        {
            ClipLine clip;

            clip.DrawClipWindow(memDC);

            Point2 p0(-400, -200);
            Point2 p1(400, 200);
            if (m_Play)
            {
                clip.LiangBarsky(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;
}

总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值