Загрузка данных
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
// ============================================================================
// 1. СТРУКТУРЫ ДАННЫХ
// ============================================================================
struct Point {
double x, y;
// Оператор сравнения точек с учетом погрешности типа double
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;
// Ребро AB и ребро BA — это одно и то же ребро
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;
};
// ============================================================================
// 2. ГЕОМЕТРИЧЕСКИЕ ХЕЛПЕРЫ
// ============================================================================
// Проверка: находится ли точка P внутри описанной окружности треугольника T
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);
}
// ============================================================================
// 3. АЛГОРИТМ БОЙЕРА — ВАТСОНА (ТРИАНГУЛЯЦИЯ ДЕЛОНЕ)
// ============================================================================
std::vector<Triangle> bowyerWatson(const std::vector<Point>& points) {
std::vector<Triangle> triangulation;
// Шаг 1: Создаем супер-треугольник (временный внешний каркас)
// Координаты выбраны с запасом для области экрана ~ 1000x1000
Point st1 = { 500, -5000 };
Point st2 = { -5000, 5000 };
Point st3 = { 5000, 5000 };
triangulation.push_back({ st1, st2, st3 });
// Шаг 2: Инкрементно добавляем каждую точку пользователя
for (const auto& p : points) {
std::vector<Triangle> badTriangles;
// Находим все треугольники, чьи окружности пробиты точкой p
for (const auto& t : triangulation) {
if (isPointInsideCircumcircle(t, p)) {
badTriangles.push_back(t);
}
}
// Шаг 3: Выделяем уникальные ребра (границу каверны)
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]);
}
}
}
// Шаг 4: Удаляем старые плохие треугольники из основной сети
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 });
}
}
// Шаг 5: Очистка. Удаляем треугольники, связанные с супер-треугольником
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;
}
// ============================================================================
// 4. ДЕМОНСТРАЦИЯ ЗАПУСКА
// ============================================================================
int main() {
// Набор тестовых точек на плоскости
std::vector<Point> points = {
{150, 200}, {300, 100}, {450, 250}, {200, 400}, {350, 350}
};
// Строим триангуляцию Делоне
std::vector<Triangle> mesh = bowyerWatson(points);
// Вывод результатов в консоль
std::cout << "Successfully built " << mesh.size() << " Delaunay triangles:\n\n";
for (size_t i = 0; i < mesh.size(); ++i) {
std::cout << "Triangle #" << i + 1 << ":\n"
<< " Vertex 1: (" << mesh[i].p1.x << ", " << mesh[i].p1.y << ")\n"
<< " Vertex 2: (" << mesh[i].p2.x << ", " << mesh[i].p2.y << ")\n"
<< " Vertex 3: (" << mesh[i].p3.x << ", " << mesh[i].p3.y << ")\n"
<< "----------------------------------------\n";
}
return 0;
}