Загрузка данных
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <vector>
#include <cmath>
#include <algorithm>
// ============================================================================
// 1. ГЕОМЕТРИЧЕСКИЕ СТРУКТУРЫ И АЛГОРИТМЫ
// ============================================================================
struct Point {
double x, y;
bool operator==(const Point& other) const {
return std::abs(x - other.x) < 1e-9 && std::abs(y - other.y) < 1e-9;
}
};
struct Edge {
Point p1, p2;
bool operator==(const Edge& other) const {
return (p1 == other.p1 && p2 == other.p2) || (p1 == other.p2 && p2 == other.p1);
}
};
struct Triangle {
Point p1, p2, p3;
};
// --- Функции для Триангуляции Делоне ---
bool isPointInsideCircumcircle(const Triangle& t, const Point& p) {
double d = 2 * (t.p1.x * (t.p2.y - t.p3.y) + t.p2.x * (t.p3.y - t.p1.y) + t.p3.x * (t.p1.y - t.p2.y));
if (std::abs(d) < 1e-9) return false;
double ux = ((t.p1.x * t.p1.x + t.p1.y * t.p1.y) * (t.p2.y - t.p3.y) +
(t.p2.x * t.p2.x + t.p2.y * t.p2.y) * (t.p3.y - t.p1.y) +
(t.p3.x * t.p3.x + t.p3.y * t.p3.y) * (t.p1.y - t.p2.y)) / d;
double uy = ((t.p1.x * t.p1.x + t.p1.y * t.p1.y) * (t.p3.x - t.p2.x) +
(t.p2.x * t.p2.x + t.p2.y * t.p2.y) * (t.p1.x - t.p3.x) +
(t.p3.x * t.p3.x + t.p3.y * t.p3.y) * (t.p2.x - t.p1.x)) / d;
double r2 = (t.p1.x - ux) * (t.p1.x - ux) + (t.p1.y - uy) * (t.p1.y - uy);
double dist2 = (p.x - ux) * (p.x - ux) + (p.y - uy) * (p.y - uy);
return dist2 <= r2;
}
bool areTrianglesEqual(const Triangle& t1, const Triangle& t2) {
auto hasPoint = [](const Triangle& t, const Point& p) {
return t.p1 == p || t.p2 == p || t.p3 == p;
};
return hasPoint(t1, t2.p1) && hasPoint(t1, t2.p2) && hasPoint(t1, t2.p3);
}
std::vector<Triangle> bowyerWatson(const std::vector<Point>& points) {
std::vector<Triangle> triangulation;
if (points.size() < 3) return triangulation;
// Супер-треугольник с запасом перекрывает рабочую область окна
Point st1 = { 400, -5000 };
Point st2 = { -5000, 5000 };
Point st3 = { 5000, 5000 };
triangulation.push_back({ st1, st2, st3 });
for (const auto& p : points) {
std::vector<Triangle> badTriangles;
for (const auto& t : triangulation) {
if (isPointInsideCircumcircle(t, p)) {
badTriangles.push_back(t);
}
}
std::vector<Edge> polygon;
for (const auto& t : badTriangles) {
Edge edges[3] = { {t.p1, t.p2}, {t.p2, t.p3}, {t.p3, t.p1} };
for (int i = 0; i < 3; ++i) {
bool isUnique = true;
for (const auto& other_t : badTriangles) {
if (areTrianglesEqual(t, other_t)) continue;
Edge other_edges[3] = { {other_t.p1, other_t.p2}, {other_t.p2, other_t.p3}, {other_t.p3, other_t.p1} };
if (edges[i] == other_edges[0] || edges[i] == other_edges[1] || edges[i] == other_edges[2]) {
isUnique = false;
break;
}
}
if (isUnique) polygon.push_back(edges[i]);
}
}
for (const auto& bad : badTriangles) {
triangulation.erase(std::remove_if(triangulation.begin(), triangulation.end(), [&](const Triangle& t) {
return areTrianglesEqual(t, bad);
}), triangulation.end());
}
for (const auto& edge : polygon) {
triangulation.push_back({ edge.p1, edge.p2, p });
}
}
triangulation.erase(std::remove_if(triangulation.begin(), triangulation.end(), [&](const Triangle& t) {
return (t.p1 == st1 || t.p1 == st2 || t.p1 == st3 ||
t.p2 == st1 || t.p2 == st2 || t.p2 == st3 ||
t.p3 == st1 || t.p3 == st2 || t.p3 == st3);
}), triangulation.end());
return triangulation;
}
// --- Функции для Выпуклой Оболочки (Алгоритм Эндрю) ---
double crossProduct(const Point& a, const Point& b, const Point& c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
std::vector<Point> buildConvexHull(std::vector<Point> pts) {
if (pts.size() < 3) return pts;
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
std::vector<Point> lower, upper;
for (const auto& p : pts) {
while (lower.size() >= 2 && crossProduct(lower[lower.size() - 2], lower.back(), p) <= 0) {
lower.pop_back();
}
lower.push_back(p);
}
for (auto it = pts.rbegin(); it != pts.rend(); ++it) {
while (upper.size() >= 2 && crossProduct(upper[upper.size() - 2], upper.back(), *it) <= 0) {
upper.pop_back();
}
upper.push_back(*it);
}
lower.pop_back();
upper.pop_back();
lower.insert(lower.end(), upper.begin(), upper.end());
return lower;
}
// ============================================================================
// 2. РЕНДЕРИНГ И ИНТЕРФЕЙС WINDOWS (WIN32 API)
// ============================================================================
// Глобальный вектор для хранения точек, которые накликал пользователь
std::vector<Point> g_points;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_LBUTTONDOWN: {
// Шаг А: Ловим координаты клика мыши
double mouseX = static_cast<double>(LOWORD(lParam));
double mouseY = static_cast<double>(HIWORD(lParam));
g_points.push_back({ mouseX, mouseY });
// Принудительно заставляем окно перерисоваться
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Включаем чистый белый фон
HBRUSH hBg = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hdc, &ps.rcPaint, hBg);
DeleteObject(hBg);
// Если точек достаточно, запускаем расчеты и отрисовку геометрии
if (g_points.size() >= 3) {
std::vector<Triangle> triangles = bowyerWatson(g_points);
std::vector<Point> hull = buildConvexHull(g_points);
// 1. Рисуем Внутреннюю Триангуляцию (Тонкие Серые Линии)
HPEN hTrianglePen = CreatePen(PS_SOLID, 1, RGB(180, 180, 180));
HGDIOBJ hOldPen = SelectObject(hdc, hTrianglePen);
for (const auto& t : triangles) {
MoveToEx(hdc, static_cast<int>(t.p1.x), static_cast<int>(t.p1.y), NULL);
LineTo(hdc, static_cast<int>(t.p2.x), static_cast<int>(t.p2.y));
LineTo(hdc, static_cast<int>(t.p3.x), static_cast<int>(t.p3.y));
LineTo(hdc, static_cast<int>(t.p1.x), static_cast<int>(t.p1.y));
}
// 2. Рисуем Выпуклую Оболочку (Жирные Красные Линии)
HPEN hHullPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hHullPen);
if (!hull.empty()) {
MoveToEx(hdc, static_cast<int>(hull[0].x), static_cast<int>(hull[0].y), NULL);
for (size_t i = 1; i < hull.size(); ++i) {
LineTo(hdc, static_cast<int>(hull[i].x), static_cast<int>(hull[i].y));
}
LineTo(hdc, static_cast<int>(hull[0].x), static_cast<int>(hull[0].y)); // Замыкаем контур
}
// Наводим порядок в памяти после перьев
SelectObject(hdc, hOldPen);
DeleteObject(hTrianglePen);
DeleteObject(hHullPen);
}
// 3. Рисуем Сами Точки (Синие Кружочки поверх линий)
HBRUSH hPointBrush = CreateSolidBrush(RGB(0, 0, 255));
HGDIOBJ hOldBrush = SelectObject(hdc, hPointBrush);
for (const auto& p : g_points) {
int r = 5; // Радиус точки для отрисовки
Ellipse(hdc, static_cast<int>(p.x) - r, static_cast<int>(p.y) - r,
static_cast<int>(p.x) + r, static_cast<int>(p.y) + r);
}
SelectObject(hdc, hOldBrush);
DeleteObject(hPointBrush);
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const wchar_t CLASS_NAME[] = L"GeometrySubsystemWindow";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Чтобы мышка была видна как стрелка
RegisterClass(&wc);
HWND hWnd = CreateWindowEx(
0, CLASS_NAME, L"Delaunay & Convex Hull Subsystem",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 1024, 768, // Окно 1024x768
NULL, NULL, hInstance, NULL
);
if (hWnd == NULL) return 0;
ShowWindow(hWnd, nShowCmd);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}