Files

163 lines
4.5 KiB
C++
Raw Permalink Normal View History

#pragma once
#include "vde/core/point.h"
2026-07-23 12:36:39 +00:00
#include "vde/core/aabb.h"
#include <vector>
2026-07-23 12:36:39 +00:00
#include <array>
namespace vde::core {
/**
* @brief 二维平面多边形
*
* 支持面积计算、点包含测试、简化和三角剖分。
* 顶点按顺序排列(闭合多边形),不存储首尾重复顶点。
*
* @note 内部假设简单多边形(无自交),非简单多边形的行为未定义
* @note 所有几何计算基于(假设的)XY 平面
*
* @ingroup core
*/
class Polygon2D {
public:
/// 构造空多边形
Polygon2D() = default;
/**
* @brief 从顶点列表构造多边形
*
* @param vertices 顶点序列(逆时针或顺时针均可)
*/
explicit Polygon2D(std::vector<Point2D> vertices) : vertices_(std::move(vertices)) {}
/// 获取顶点列表
[[nodiscard]] const std::vector<Point2D>& vertices() const { return vertices_; }
/// 获取顶点数量
[[nodiscard]] size_t size() const { return vertices_.size(); }
/// 多边形是否为空(< 3 个顶点视为退化)
[[nodiscard]] bool empty() const { return vertices_.empty(); }
/**
* @brief 有向面积(Shoelace 公式)
*
* 正值表示逆时针(CCW),负值表示顺时针(CW),零表示自交或退化。
*
* @return 有向面积
*
* @code{.cpp}
* Polygon2D poly({{0,0}, {1,0}, {0,1}});
* double sa = poly.signed_area(); // > 0 (CCW)
* @endcode
*/
[[nodiscard]] double signed_area() const;
/**
* @brief 绝对面积
* @return |signed_area()|
*/
[[nodiscard]] double area() const { return std::abs(signed_area()); }
/**
* @brief 周长(各边长之和)
* @return 总边长
*/
2026-07-23 12:36:39 +00:00
[[nodiscard]] double perimeter() const;
/**
* @brief 面积加权质心
*
* 使用多边形三角剖分的面积加权平均计算质心。
* 适用于非自交的任意多边形。
*
* @return 质心坐标
*/
2026-07-23 12:36:39 +00:00
[[nodiscard]] Point2D centroid() const;
/**
* @brief 二维轴对齐包围盒
*
* 返回 AABBz=0),min/max 的 z 分量均为 0。
*
* @return 包围盒
*/
2026-07-23 12:36:39 +00:00
[[nodiscard]] AABB<double> bounding_box() const;
/**
* @brief 点包含测试(射线法)
*
* 从测试点向右发射水平射线,计算与多边形边的交点数量。
* 奇数次交点在内部,偶数次在外部。
*
* @param p 测试点
* @return true 若点在多边形内部或边界上
*
* @note 边界上的点也返回 true
*/
[[nodiscard]] bool contains(const Point2D& p) const;
/**
* @brief 多边形是否为逆时针方向
* @return signed_area() > 0
*/
[[nodiscard]] bool is_ccw() const { return signed_area() > 0; }
/**
* @brief Douglas-Peucker 简化
*
* 用递归的 Douglas-Peucker 算法减少顶点数量,
* 保证简化后多边形与原多边形的最大偏差不超过 tolerance。
*
* @param tolerance 允许的最大偏差
* @return 简化后的新多边形
*
* @code{.cpp}
* // 将 1000 个顶点的多边形简化为约 100 个
* auto simple = poly.simplify(0.01);
* @endcode
*
* @see triangulate
*/
2026-07-23 12:36:39 +00:00
[[nodiscard]] Polygon2D simplify(double tolerance) const;
/**
* @brief 耳切法三角剖分(Ear Clipping
*
* 将简单多边形剖分为三角形,返回每个三角形的顶点索引三元组。
*
* @return 三角形列表,每个元素为 {i, j, k} 表示顶点索引
*
* @pre 多边形为简单多边形(无自交)
* @pre 顶点为逆时针顺序(CCW)
*
* @note 时间复杂度 O(n²),适用于顶点数 < 1000 的多边形
*
* @code{.cpp}
* Polygon2D poly(CCW vertices);
* auto tris = poly.triangulate();
* for (auto& t : tris) {
* // 顶点: poly.vertices()[t[0]], t[1], t[2]
* }
* @endcode
*
* @see simplify
*/
2026-07-23 12:36:39 +00:00
[[nodiscard]] std::vector<std::array<int, 3>> triangulate() const;
/**
* @brief Andrew's Monotone Chain 二维凸包
*
* 计算点集的凸包,返回逆时针排列的凸包多边形。
* 时间复杂度 O(n log n)。
*
* @param points 输入点集
* @return 凸包多边形(CCW,不含共线中间点)
*
* @see convex_hull_2d
*/
2026-07-23 12:36:39 +00:00
static Polygon2D convex_hull_2d(const std::vector<Point2D>& points);
private:
std::vector<Point2D> vertices_;
};
} // namespace vde::core