参考: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;
}