Загрузка данных
#include <windows.h>
#include <vector>
#include <cmath>
#include <cstring>
struct Pt
{
double x, y;
};
using Poly = std::vector<Pt>;
// =====================================================
// ГЛОБАЛЬНЫЕ ДАННЫЕ
// =====================================================
static Poly g_arrow;
static bool g_visible = true;
static RECT g_clip =
{
150, 100,
550, 430
};
static bool g_dragging = false;
static POINT g_dragStart;
// =====================================================
// СОЗДАНИЕ ФИГУРЫ
// =====================================================
static void BuildArrow()
{
g_arrow =
{
{180, 220},
{340, 220},
{340, 180},
{520, 265},
{340, 350},
{340, 310},
{180, 310}
};
}
// =====================================================
// ГЕОМЕТРИЯ
// =====================================================
static double Cross(Pt a, Pt b, Pt c)
{
return
(b.x - a.x) * (c.y - a.y) -
(b.y - a.y) * (c.x - a.x);
}
static bool Intersect(
Pt p1,
Pt p2,
Pt e1,
Pt e2,
Pt& out)
{
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
double ex = e2.x - e1.x;
double ey = e2.y - e1.y;
double d = dx * ey - dy * ex;
if (fabs(d) < 1e-9)
return false;
double t =
((e1.x - p1.x) * ey -
(e1.y - p1.y) * ex) / d;
out.x = p1.x + t * dx;
out.y = p1.y + t * dy;
return true;
}
// =====================================================
// ОТСЕЧЕНИЕ ОДНОЙ ГРАНЬЮ
// =====================================================
static Poly ClipByEdge(
const Poly& poly,
Pt e1,
Pt e2)
{
Poly out;
int n = (int)poly.size();
for (int i = 0; i < n; ++i)
{
Pt cur = poly[i];
Pt nxt = poly[(i + 1) % n];
double cc = Cross(e1, e2, cur);
double cn = Cross(e1, e2, nxt);
bool curInside = cc >= 0;
bool nxtInside = cn >= 0;
if (curInside)
{
out.push_back(cur);
if (!nxtInside)
{
Pt p;
Intersect(cur, nxt, e1, e2, p);
out.push_back(p);
}
}
else if (nxtInside)
{
Pt p;
Intersect(cur, nxt, e1, e2, p);
out.push_back(p);
}
}
return out;
}
// =====================================================
// АЛГОРИТМ ОТСЕЧЕНИЯ
// =====================================================
static Poly WeilerAtherton(
const Poly& subject,
RECT clip)
{
double l = clip.left;
double r = clip.right;
double t = clip.top;
double b = clip.bottom;
Poly res = subject;
res = ClipByEdge(res, {l, b}, {l, t});
res = ClipByEdge(res, {l, t}, {r, t});
res = ClipByEdge(res, {r, t}, {r, b});
res = ClipByEdge(res, {r, b}, {l, b});
return res;
}
// =====================================================
// РИСОВАНИЕ МНОГОУГОЛЬНИКА
// =====================================================
static void DrawPoly(
HDC hdc,
const Poly& poly,
COLORREF color,
bool fill)
{
if (poly.size() < 2)
return;
std::vector<POINT> pts(poly.size());
for (size_t i = 0; i < poly.size(); ++i)
{
pts[i].x = (LONG)poly[i].x;
pts[i].y = (LONG)poly[i].y;
}
HPEN pen =
CreatePen(PS_SOLID, 2, color);
HBRUSH brush =
fill
? CreateSolidBrush(color)
: (HBRUSH)GetStockObject(NULL_BRUSH);
HPEN oldPen =
(HPEN)SelectObject(hdc, pen);
HBRUSH oldBrush =
(HBRUSH)SelectObject(hdc, brush);
Polygon(hdc, pts.data(), (int)pts.size());
SelectObject(hdc, oldPen);
SelectObject(hdc, oldBrush);
DeleteObject(pen);
if (fill)
DeleteObject(brush);
}
// =====================================================
// РИСОВАНИЕ СЦЕНЫ
// =====================================================
static void DrawScene(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
HDC mem = CreateCompatibleDC(hdc);
HBITMAP bmp =
CreateCompatibleBitmap(
hdc,
rc.right,
rc.bottom);
HBITMAP oldBmp =
(HBITMAP)SelectObject(mem, bmp);
// фон
HBRUSH bg =
CreateSolidBrush(RGB(0, 0, 0));
FillRect(mem, &rc, bg);
DeleteObject(bg);
// окно отсечения
{
HPEN pen =
CreatePen(
PS_DASH,
1,
RGB(70, 70, 70));
HPEN oldPen =
(HPEN)SelectObject(mem, pen);
SelectObject(
mem,
GetStockObject(NULL_BRUSH));
Rectangle(
mem,
g_clip.left,
g_clip.top,
g_clip.right,
g_clip.bottom);
SelectObject(mem, oldPen);
DeleteObject(pen);
}
// фигура
if (g_visible)
{
DrawPoly(
mem,
g_arrow,
RGB(255,255,255),
false);
Poly clipped =
WeilerAtherton(
g_arrow,
g_clip);
if (!clipped.empty())
{
DrawPoly(
mem,
clipped,
RGB(255,0,0),
true);
}
}
// текст
SetBkMode(mem, TRANSPARENT);
SetTextColor(
mem,
RGB(200,200,200));
HFONT font =
CreateFontA(
16,
0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
DEFAULT_PITCH,
"Consolas");
HFONT oldFont =
(HFONT)SelectObject(mem, font);
const char* hint =
"LMB - move | Del - delete | R - reset | Esc - exit";
TextOutA(
mem,
10,
rc.bottom - 24,
hint,
(int)strlen(hint));
SelectObject(mem, oldFont);
DeleteObject(font);
// вывод
BitBlt(
hdc,
0,
0,
rc.right,
rc.bottom,
mem,
0,
0,
SRCCOPY);
SelectObject(mem, oldBmp);
DeleteObject(bmp);
DeleteDC(mem);
EndPaint(hwnd, &ps);
}
// =====================================================
// ОКОННАЯ ПРОЦЕДУРА
// =====================================================
static LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp)
{
switch (msg)
{
case WM_PAINT:
{
DrawScene(hwnd);
return 0;
}
case WM_LBUTTONDOWN:
{
g_dragging = true;
g_dragStart =
{
(short)LOWORD(lp),
(short)HIWORD(lp)
};
SetCapture(hwnd);
return 0;
}
case WM_MOUSEMOVE:
{
if (g_dragging)
{
int dx =
(short)LOWORD(lp) -
g_dragStart.x;
int dy =
(short)HIWORD(lp) -
g_dragStart.y;
for (auto& p : g_arrow)
{
p.x += dx;
p.y += dy;
}
g_dragStart =
{
(short)LOWORD(lp),
(short)HIWORD(lp)
};
InvalidateRect(
hwnd,
NULL,
FALSE);
}
return 0;
}
case WM_LBUTTONUP:
{
g_dragging = false;
ReleaseCapture();
return 0;
}
case WM_KEYDOWN:
{
if (wp == VK_DELETE)
{
g_visible = false;
InvalidateRect(
hwnd,
NULL,
FALSE);
}
if (wp == 'R')
{
BuildArrow();
g_visible = true;
InvalidateRect(
hwnd,
NULL,
FALSE);
}
if (wp == VK_ESCAPE)
{
PostQuitMessage(0);
}
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(
hwnd,
msg,
wp,
lp);
}
// =====================================================
// ТОЧКА ВХОДА
// =====================================================
int main()
{
HINSTANCE hInst =
GetModuleHandleA(NULL);
BuildArrow();
WNDCLASSEXA wc = {};
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground =
(HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "WA_Demo";
RegisterClassExA(&wc);
HWND hwnd =
CreateWindowExA(
0,
"WA_Demo",
"Weiler-Atherton Clipping",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
750,
520,
NULL,
NULL,
hInst,
NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}