#pragma once /** * @file modeling.h * @brief 高层 B-Rep 建模 API * * 提供 B-Rep 实体的创建和编辑操作,类比于 CAD 系统的特征建模功能。 * * ## 功能概览 * * | 类别 | 操作 | * |----------------|---------------------------------------------| * | 基本体 | make_box, make_cylinder, make_sphere | * | 扫掠特征 | extrude, revolve, sweep, loft | * | 编辑操作 | fillet, chamfer, shell | * * ## 使用模式 * * 每个函数返回一个新的 BrepModel(值语义),适合链式构建: * * @code{.cpp} * auto body = make_box(10, 5, 3); * // 或者更复杂的构建: * auto profile = NURBS 曲线; * auto swept = extrude(profile, Vector3D(0, 0, 10)); * auto rounded = fillet(swept, 0, 2.0); * @endcode * * @ingroup brep */ #include "vde/core/point.h" #include "vde/brep/brep.h" #include "vde/mesh/halfedge_mesh.h" namespace vde::brep { using core::Point3D; using core::Vector3D; using core::AABB3D; // ═══════════════════════════════════════════════ // 扫掠特征(NURBS 轮廓) // ═══════════════════════════════════════════════ /** * @brief 沿方向挤出平面轮廓 * * 将平面曲线沿给定方向扫掠,生成柱状实体。 * * **算法:** 曲线的每个点沿方向向量平移,两端加盖平面形成封闭实体。 * * @param profile 轮廓曲线(必须位于一个平面内) * @param dir 挤出方向向量(其长度决定挤出距离) * @return 挤出后的 B-Rep 实体 * * @pre profile 必须是平面曲线(存在于某个平面内)且闭合 * * @code{.cpp} * // 沿 Z 轴挤出 5 个单位 * auto circle = NurbsCurve::create_circle(Point3D(0,0,0), 2.0); * auto cylinder = extrude(circle, Vector3D(0, 0, 5)); * @endcode * * @see revolve 旋转扫掠 * @see sweep 沿任意路径扫掠 */ [[nodiscard]] BrepModel extrude(const curves::NurbsCurve& profile, const Vector3D& dir); /** * @brief 绕轴旋转平面轮廓 * * 将轮廓绕给定轴旋转,生成旋转体(如轮子、瓶子等)。 * * @param profile 轮廓曲线(通常位于轴的半平面中) * @param axis_origin 旋转轴上的一个点 * @param axis_dir 旋转轴方向(单位向量) * @param angle_rad 旋转角度(弧度,默认 2π = 完整的旋转体) * @return 旋转后的 B-Rep 实体 * * @pre profile 必须在轴的同一侧(不应跨越轴线) * * @code{.cpp} * // 绕 Y 轴旋转半圆,生成球体的一半 * NurbsCurve semicircle = 在 XZ 平面中的半圆弧; * auto half_sphere = revolve(semicircle, Point3D(0,0,0), * Vector3D(0,1,0), M_PI); * @endcode * * @see extrude 线性挤出 * @see make_sphere 直接创建球体 */ [[nodiscard]] BrepModel revolve(const curves::NurbsCurve& profile, const Point3D& axis_origin, const Vector3D& axis_dir, double angle_rad = 2.0 * M_PI); /** * @brief 沿任意路径扫掠轮廓 * * 轮廓沿一条空间路径曲线运动,生成管道或异形截面杆。 * * @param profile 截面轮廓(需位于路径起点的垂直平面内) * @param path 扫掠路径曲线 * @return 扫掠后的 B-Rep 实体 * * @note 路径曲率过大处可能出现自交,需要控制路径的光滑性。 * * @code{.cpp"} * auto circle = NurbsCurve::create_circle(Point3D(0,0,0), 0.5); * auto helix = NurbsCurve::create_helix(// ...); * auto pipe = sweep(circle, helix); * @endcode * * @see extrude 直线挤出 * @see revolve 旋转扫掠 */ [[nodiscard]] BrepModel sweep(const curves::NurbsCurve& profile, const curves::NurbsCurve& path); /** * @brief 在两个或多个轮廓曲线之间放样 * * 通过线性或平滑插值连接多个平行或非平行轮廓, * 生成过渡实体。 * * @param profiles 轮廓曲线列表(至少 2 条) * @return 放样后的 B-Rep 实体 * * @pre profiles.size() ≥ 2 * @pre 所有轮廓应是平面曲线(逐一) * * @code{.cpp} * auto profile_a = NurbsCurve::create_circle(Point3D(0,0,0), 5.0); * auto profile_b = NurbsCurve::create_circle(Point3D(0,0,10), 2.0); * auto cone = loft({profile_a, profile_b}); * @endcode * * @see extrude 单轮廓挤出 */ [[nodiscard]] BrepModel loft(const std::vector& profiles); // ═══════════════════════════════════════════════ // 扫掠特征(Mesh 轮廓 — VDE-021) // 将 HalfedgeMesh 自动转为边界 NURBS 后调用对应的 NURBS 扫掠 // ═══════════════════════════════════════════════ /** * @brief 从网格轮廓沿方向挤出(VDE-021) * * 自动提取网格的闭合边界环,拟合为 NURBS 曲线后沿方向挤出。 * * @param profileMesh 轮廓网格(应为带边界的平面网格) * @param dir 挤出方向向量 * @return 挤出后的 B-Rep 实体 * * @see mesh_boundary_to_curve 边界提取 */ [[nodiscard]] BrepModel extrude(const mesh::HalfedgeMesh& profileMesh, const Vector3D& dir); /** * @brief 从网格轮廓绕轴旋转(VDE-021) * * 自动提取网格边界环,拟合 NURBS 后绕轴旋转。 * * @param profileMesh 轮廓网格 * @param origin 旋转轴原点 * @param axis 旋转轴方向 * @param angle 旋转角度(弧度) * @return 旋转后的 B-Rep 实体 */ [[nodiscard]] BrepModel revolve(const mesh::HalfedgeMesh& profileMesh, const Point3D& origin, const Vector3D& axis, double angle = 2.0 * M_PI); /** * @brief 从网格轮廓沿路径扫掠(VDE-021) * * @param profileMesh 截面轮廓网格 * @param path 扫掠路径曲线 * @return 扫掠后的 B-Rep 实体 */ [[nodiscard]] BrepModel sweep(const mesh::HalfedgeMesh& profileMesh, const curves::NurbsCurve& path); /** * @brief 多个网格轮廓之间放样(VDE-021) * * 对每个网格提取边界环,拟合 NURBS 后放样。 * * @param profiles 轮廓网格列表(至少 2 个) * @return 放样后的 B-Rep 实体 */ [[nodiscard]] BrepModel loft(const std::vector& profiles); // ═══════════════════════════════════════════════ // 基本体 // ═══════════════════════════════════════════════ /** * @brief 创建立方体 * * 以原点为中心、轴对齐的立方体。 * * @param w 宽度(X 方向),必须 > 0 * @param h 高度(Y 方向),必须 > 0 * @param d 深度(Z 方向),必须 > 0 * @return 实心立方体 B-Rep * * @code{.cpp} * auto box = make_box(10, 5, 3); // 10×5×3 的盒子 * @endcode * * @see make_cylinder 圆柱体 * @see make_sphere 球体 */ [[nodiscard]] BrepModel make_box(double w, double h, double d, const Point3D& center = Point3D(0, 0, 0)); /** * @brief 创建圆柱体 * * 沿 Y 轴、以原点为中心的圆柱体。 * * @param radius 截面半径,必须 > 0 * @param height 总高度,沿 Y 轴从 -h/2 到 +h/2 * @param segments 截面分段数(边数,默认 32) * @return 实心圆柱体 B-Rep * * @note segments 越大圆柱越光滑,但面和边数增加。 * * @code{.cpp} * auto cyl = make_cylinder(2.0, 10.0, 64); // 高精度圆柱 * @endcode * * @see make_box 立方体 * @see make_sphere 球体 */ [[nodiscard]] BrepModel make_cylinder(double radius, double height, int segments = 32, const Point3D& center = Point3D(0, 0, 0)); /** * @brief 创建球体 * * 以原点为中心的球体。 * * @param radius 球半径,必须 > 0 * @param segments_u 经度方向分段数(默认 32) * @param segments_v 纬度方向分段数(默认 16) * @return 实心球体 B-Rep * * @code{.cpp} * auto sphere = make_sphere(5.0, 32, 32); * @endcode * * @see make_cylinder 圆柱体 */ [[nodiscard]] BrepModel make_sphere(double radius, int segments_u = 32, int segments_v = 16, const Point3D& center = Point3D(0, 0, 0)); /** * @brief 创建圆锥/圆台 * * 沿 Y 轴、以给定位置为中心的圆锥或圆台。 * * @param r1 底部半径,必须 ≥ 0 * @param r2 顶部半径,必须 ≥ 0(r2=0 时为圆锥) * @param height 总高度,沿 Y 轴从 -h/2 到 +h/2 * @param segments 截面分段数(边数,默认 32) * @param center 几何中心位置(默认原点) * @return 实心圆台/圆锥 B-Rep * * @code{.cpp} * auto cone = make_cone(3.0, 1.0, 8.0); // 底部半径 3,顶部半径 1 * auto pointy = make_cone(2.0, 0.0, 5.0); // 圆锥(顶部半径 0) * @endcode * * @see make_cylinder 圆柱体 */ [[nodiscard]] BrepModel make_cone(double r1, double r2, double height, int segments = 32, const Point3D& center = Point3D(0, 0, 0)); /** * @brief 创建圆环体 * * 以给定位置为中心的圆环体(torus)。 * * @param rMajor 中心环半径(从圆环中心到管中心的距离) * @param rMinor 管半径 * @param segments_u 绕中心环的分段数(默认 32) * @param segments_v 绕管截面的分段数(默认 16) * @param center 几何中心位置(默认原点) * @return 实心圆环体 B-Rep * * @pre rMajor > rMinor > 0 * * @code{.cpp} * auto torus = make_torus(5.0, 1.0, 64, 32); * @endcode */ [[nodiscard]] BrepModel make_torus(double rMajor, double rMinor, int segments_u = 32, int segments_v = 16, const Point3D& center = Point3D(0, 0, 0)); // ═══════════════════════════════════════════════ // 编辑操作 // ═══════════════════════════════════════════════ /** * @brief 对边做圆角处理 * * 在指定的边上施加恒定半径的圆角(倒圆)。 * 适用于凹边和凸边。 * * @param body 输入实体 * @param edge_id 要倒圆的边 ID * @param radius 圆角半径,必须 > 0 * @return 圆角后的新实体 * * @note 圆角半径不能超过相邻面的最小宽度,否则会产生自交。 * * @code{.cpp} * auto box = make_box(10, 5, 3); * auto rounded = fillet(box, 0, 2.0); // 对边 0 做半径 2 的圆角 * @endcode * * @see fillet_variable 变半径圆角 * @see chamfer 倒角 * @see shell 抽壳 */ [[nodiscard]] BrepModel fillet(const BrepModel& body, int edge_id, double radius); /** * @brief 变半径圆角 * * 沿边的参数方向在 r_start 和 r_end 之间线性插值半径, * 生成平滑过渡的圆角曲面。 * * @param body 输入实体 * @param edge_id 要倒圆的边 ID * @param r_start 边起点处的圆角半径 * @param r_end 边终点处的圆角半径 * @param samples 沿边的横截面采样数(默认 8) * @return 变半径圆角后的新实体 * * @pre r_start ≥ 0, r_end ≥ 0,且至少一个 > 0 * @note samples 越大截面过渡越光滑,但面和边数增加。 * * @code{.cpp} * auto box = make_box(10, 5, 3); * auto result = fillet_variable(box, 0, 0.5, 2.0, 12); * // 边 0 从 0.5 到 2.0 逐渐变化的圆角 * @endcode * * @see fillet 恒定半径圆角 */ [[nodiscard]] BrepModel fillet_variable(const BrepModel& body, int edge_id, double r_start, double r_end, int samples = 8); /** * @brief 对边做倒角 * * 将尖锐边替换为 45° 斜面。 * * @param body 输入实体 * @param edge_id 要倒角的边 ID * @param distance 倒角距离(从原边沿两个面各偏移的距离) * @return 倒角后的新实体 * * @pre distance > 0 * * @see fillet 圆角 */ [[nodiscard]] BrepModel chamfer(const BrepModel& body, int edge_id, double distance); /** * @brief 抽壳(挖空实体) * * 将实体变为等厚度的壳,去除指定面形成开口。 * * @param body 输入实心实体 * @param face_id 要移除的面 ID(形成开口),-1 表示封闭壳(不开口) * @param thickness 壁厚(正值 = 向外偏移,负值 = 向内偏移) * @return 抽壳后的新实体 * * @note 壁厚不应超过实体最小尺寸的一半。 * @note 当 face_id = -1 时,结果是一个封闭的壳(中空但没有开口)。 * * @code{.cpp"} * auto box = make_box(10, 5, 3); * auto shelled = shell(box, 0, 0.5); // 移除面 0,壁厚 0.5 * @endcode * * @see fillet 圆角 */ [[nodiscard]] BrepModel shell(const BrepModel& body, int face_id, double thickness); // ═══════════════════════════════════════════════ // 拓扑查询(基于几何位置) // ═══════════════════════════════════════════════ /** * @brief 根据几何位置查找最近的 edge ID * * 遍历所有边,返回其端点中点距离给定点最近的边的 ID。 * 用于 mesh→BrepModel 转换后重新定位拓扑元素。 * * @param body 输入实体 * @param nearPoint 参考点(通常为 mesh edge 中点) * @return 最近的边 ID,若 body 无边则返回 -1 * * @see get_face_index_by_position */ [[nodiscard]] int get_edge_index_by_position(const BrepModel& body, const Point3D& nearPoint); /** * @brief 根据几何位置查找最近的 face ID * * 遍历所有面,返回其边界盒中心距离给定点最近的面 ID。 * 用于 mesh→BrepModel 转换后重新定位拓扑元素。 * * @param body 输入实体 * @param nearPoint 参考点(通常为 mesh face 中心) * @return 最近的面 ID,若 body 无面则返回 -1 * * @see get_edge_index_by_position */ [[nodiscard]] int get_face_index_by_position(const BrepModel& body, const Point3D& nearPoint); /** * @brief 基于几何位置对边做圆角(VDE-022 方案二) * * 自动查找距离 nearPoint 最近的边并对其倒圆角。 * 适用于 mesh→BrepModel 转换后的场景(edge ID 已发生变化)。 * * @param body 输入实体 * @param nearPoint 参考点(靠近目标边的任意点) * @param radius 圆角半径 * @return 圆角后的实体(若未找到边则返回原实体) * * @see fillet 基于 ID 的圆角 */ [[nodiscard]] BrepModel fillet_by_position(const BrepModel& body, const Point3D& nearPoint, double radius); /** * @brief 基于几何位置对边做倒角(VDE-022 方案二) * * @param body 输入实体 * @param nearPoint 参考点 * @param distance 倒角距离 * @return 倒角后的实体 * * @see chamfer 基于 ID 的倒角 */ [[nodiscard]] BrepModel chamfer_by_position(const BrepModel& body, const Point3D& nearPoint, double distance); } // namespace vde::brep