Загрузка данных
// ============================================================================
// Лабораторная работа №5 — Выпуклая оболочка (алгоритм Грэхема)
// Windows GUI приложение на Win32 API
// ============================================================================
#include <windows.h>
#include <vector>
#include <algorithm>
using namespace std;
// ============================================================================
// Константы
// ============================================================================
const int BUTTON_ID = 1;
const int WINDOW_WIDTH = 900;
const int WINDOW_HEIGHT = 650;
const int BUTTON_X = 10;
const int BUTTON_Y = 10;
const int BUTTON_WIDTH = 180;
const int BUTTON_HEIGHT = 35;
const int TEXT_X = 210;
const int TEXT_Y = 18;
const int DRAW_AREA_Y = 55;
const int POINT_RADIUS = 4;
const int HULL_PEN_WIDTH = 3;
// ============================================================================
// Структуры данных
// ============================================================================
struct Point {
int x;
int y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
bool operator<(const Point& other) const {
if (x != other.x) return x < other.x;
return y < other.y;
}
};
// ============================================================================
// Глобальные переменные
// ============================================================================
vector<Point> points;
vector<Point> hull;
HWND buttonBuild;
// ============================================================================
// Вспомогательные функции (геометрия)
// ============================================================================
/// Векторное произведение (a->b) x (a->c)
long long cross(const Point& a, const Point& b, const Point& c) {
return 1LL * (b.x - a.x) * (c.y - a.y)
- 1LL * (b.y - a.y) * (c.x - a.x);
}
/// Квадрат расстояния между двумя точками
long long dist2(const Point& a, const Point& b) {
return 1LL * (a.x - b.x) * (a.x - b.x)
+ 1LL * (a.y - b.y) * (a.y - b.y);
}
// ============================================================================
// Алгоритм Грэхема — построение выпуклой оболочки
// ============================================================================
vector<Point> grahamHull(vector<Point> p) {
// Базовые случаи
if (p.size() <= 1) return p;
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
if (p.size() <= 2) return p;
// Находим самую нижнюю (и левую) точку
int start = 0;
for (int i = 1; i < (int)p.size(); i++) {
if (p[i].y < p[start].y ||
(p[i].y == p[start].y && p[i].x < p[start].x)) {
start = i;
}
}
swap(p[0], p[start]);
// Сортируем остальные точки по полярному углу
Point base = p[0];
sort(p.begin() + 1, p.end(), [base](const Point& a, const Point& b) {
long long cr = cross(base, a, b);
if (cr == 0) return dist2(base, a) < dist2(base, b);
return cr > 0;
});
// Строим оболочку с помощью стека
vector<Point> st;
for (const Point& pt : p) {
while (st.size() >= 2 &&
cross(st[st.size() - 2], st[st.size() - 1], pt) <= 0) {
st.pop_back();
}
st.push_back(pt);
}
return st;
}
// ============================================================================
// Функции отрисовки
// ============================================================================
void drawPoint(HDC hdc, const Point& p) {
Ellipse(hdc,
p.x - POINT_RADIUS, p.y - POINT_RADIUS,
p.x + POINT_RADIUS, p.y + POINT_RADIUS);
}
void drawHull(HDC hdc) {
if (hull.size() < 2) return;
HPEN pen = CreatePen(PS_SOLID, HULL_PEN_WIDTH, RGB(255, 0, 0));
HPEN oldPen = (HPEN)SelectObject(hdc, pen);
for (int i = 0; i < (int)hull.size(); i++) {
Point a = hull[i];
Point b = hull[(i + 1) % hull.size()];
MoveToEx(hdc, a.x, a.y, NULL);
LineTo(hdc, b.x, b.y);
}
SelectObject(hdc, oldPen);
DeleteObject(pen);
}
// ============================================================================
// Оконная процедура
// ============================================================================
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
// --- Создание окна: инициализация кнопки ---
case WM_CREATE:
buttonBuild = CreateWindowW(
L"BUTTON", L"Построить оболочку",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
BUTTON_X, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT,
hwnd, (HMENU)(INT_PTR)BUTTON_ID, NULL, NULL);
break;
// --- Клик левой кнопкой мыши: добавление точки ---
case WM_LBUTTONDOWN: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if (y > DRAW_AREA_Y) {
points.push_back({x, y});
hull.clear();
InvalidateRect(hwnd, NULL, TRUE);
}
break;
}
// --- Команды от элементов управления ---
case WM_COMMAND:
if (LOWORD(wParam) == BUTTON_ID) {
hull = grahamHull(points);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
// --- Перерисовка окна ---
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
SetBkMode(hdc, TRANSPARENT);
// Подсказка
const wchar_t text[] = L"Кликайте мышкой для добавления точек";
TextOutW(hdc, TEXT_X, TEXT_Y, text, lstrlenW(text));
// Точки (синие)
HBRUSH pointBrush = CreateSolidBrush(RGB(0, 0, 255));
HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, pointBrush);
for (const Point& p : points) {
drawPoint(hdc, p);
}
SelectObject(hdc, oldBrush);
DeleteObject(pointBrush);
// Оболочка (красная)
drawHull(hdc);
EndPaint(hwnd, &ps);
break;
}
// --- Закрытие окна ---
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
// ============================================================================
// Точка входа
// ============================================================================
int main() {
HINSTANCE hInstance = GetModuleHandleW(NULL);
// Регистрация класса окна
const wchar_t CLASS_NAME[] = L"ConvexHullWindow";
WNDCLASSW wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClassW(&wc)) {
MessageBoxW(NULL, L"Ошибка регистрации класса окна",
L"Ошибка", MB_OK | MB_ICONERROR);
return 1;
}
// Создание окна
HWND hwnd = CreateWindowExW(
0, CLASS_NAME,
L"Лабораторная работа №5 — Выпуклая оболочка. Алгоритм Грэхема",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
MessageBoxW(NULL, L"Ошибка создания окна",
L"Ошибка", MB_OK | MB_ICONERROR);
return 1;
}
// Показ и запуск цикла сообщений
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg = {};
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}