参考https://www.bilibili.com/video/BV1AP411n7xb
算法原理
算法设计
源码
// 19-中点分割裁剪算法
// 参考 https://www.bilibili.com/video/BV1AP411n7xb
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"19-中点分割裁剪算法"
#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) {}
friend Point2 operator+(Point2 p0 , Point2 p1)
{
return Point2(p0.x + p1.x, p0.y + p1.y);
}
friend Point2 operator/(Point2 p, double n)
{
return Point2(p.x/2, p.y / 2);
}
};
class TrimLine
{
public:
int wxl; //窗口左
int wxr; //窗口右
int wyt; //窗口上
int wyb; //窗口下
public:
TrimLine()
{
wxl = -300;
wxr = 300;
wyt = 100;
wyb = -100;
};
~TrimLine() {};
void MidPointDivider(Point2& P0, Point2& P1) // 中点分割
{
Point2 p0 = P0;
Point2 p1 = P1;
Point2 pm = (p0 + p1) / 2;
Encode(pm);
while (fabs(pm.x-p0.x)> 1e-4 ||fabs(pm.y-p0.y)>1e-4) // 判断算法结束
{
if ((p0.rc & pm.rc) != 0)
{
p0 = pm;
}
else
{
p1 = pm;
}
pm = (p0 + p1) / 2;
Encode(pm);
}
P0 = pm;
}
// 引用 & 改变实参的值
void Cohen_Sutherland(Point2& p0, Point2& p1) // 裁剪线段
{
Encode(p0); Encode(p1);
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;
}
MidPointDivider(p0,p1);
}
}
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* 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);
// 绘制图形
{
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;
}