#pragma once #include "vde/core/point.h" #include "vde/core/aabb.h" #include #include #include #include namespace vde::mesh { using core::Point3D; using core::Vector3D; using core::AABB3D; // ─────────────────────────────────────────────────────────── // PointCloud 点云类 // ─────────────────────────────────────────────────────────── /** * @brief 点云数据结构 * * 存储顶点位置、法线、颜色(可选)及包围盒信息。 * 支持 PLY/E57/PTX 加载,体素降采样、统计离群点剔除、kNN 法线估计。 * * @ingroup mesh */ class PointCloud { public: std::vector vertices; ///< 顶点位置 std::vector normals; ///< 顶点法线(归一化,空数组表示未计算) std::vector> colors; ///< RGB 颜色(0-255,空数组表示无颜色) PointCloud() = default; /// 从顶点列表构造(无颜色/法线) explicit PointCloud(std::vector pts) : vertices(std::move(pts)) {} /// 顶点数 [[nodiscard]] size_t size() const { return vertices.size(); } /// 是否有法线 [[nodiscard]] bool has_normals() const { return !normals.empty(); } /// 是否有颜色 [[nodiscard]] bool has_colors() const { return !colors.empty(); } /// 包围盒 [[nodiscard]] AABB3D bounds() const; // ── I/O ────────────────────────────────────────────── /// 加载 PLY 点云文件(ASCII 或 binary little-endian) static PointCloud load_ply(const std::string& path); /// 加载 E57 点云文件(简化解析:仅提取 CartesianBounds + points3D) static PointCloud load_e57(const std::string& path); /// 加载 PTX 格式点云(Leica 扫描仪原始格式) static PointCloud load_ptx(const std::string& path); /// 保存为 PLY 文件 bool save_ply(const std::string& path) const; // ── 滤波 / 降采样 ──────────────────────────────────── /** * @brief 体素降采样 * @param voxel_size 体素边长 * @return 降采样后的点云(每个体素用质心代替) */ [[nodiscard]] PointCloud voxel_downsample(double voxel_size) const; /** * @brief 统计离群点剔除(SOR) * @param k 邻域点数(默认 6) * @param std_ratio 标准差倍率阈值(默认 1.0),超过 mean+k*std_ratio 的点标记为离群 * @return 剔除离群点后的点云 */ [[nodiscard]] PointCloud statistical_outlier_removal(int k = 6, double std_ratio = 1.0) const; // ── 法线估计 ────────────────────────────────────────── /** * @brief kNN 法线估计 * * 对每个点,找到 k 个最近邻点,用 PCA 估计法线(最小特征值对应方向)。 * 法线方向通过视角向量(点→原点方向)统一朝向。 * * @param k 最近邻点数(≥ 3) * @return 估计了法线的新点云 */ [[nodiscard]] PointCloud normal_estimation(int k = 10) const; // ── 曲率感知采样 ────────────────────────────────────── /** * @brief 曲率感知降采样 * * 计算每点局部曲率近似,按曲率加权采样:高曲率区域保留更多点。 * * @param target_count 目标点数 * @return 采样后的索引数组 */ [[nodiscard]] std::vector curvature_aware_sampling(size_t target_count) const; // ── 平滑滤波 ────────────────────────────────────────── /** * @brief 双边滤波平滑 * * 对点云位置进行法向加权的双边滤波去噪,保持尖锐特征。 * * @param sigma_c 空间高斯 σ(0 = 自动按平均点距计算) * @param sigma_n 法向高斯 σ(弧度) * @param iterations 迭代次数 * @return 平滑后的新点云 */ [[nodiscard]] PointCloud smoothing_filter(double sigma_c = 0.0, double sigma_n = 0.3, int iterations = 3) const; private: /** 构建 k-d 树索引,返回每个顶点的 k 最近邻索引 */ [[nodiscard]] std::vector> k_nearest_neighbors(size_t k) const; /** 协方差矩阵 PCA:计算三个特征值,返回最小特征值对应的特征向量 */ [[nodiscard]] static Vector3D pca_normal(const std::vector& local_pts); }; } // namespace vde::mesh