feat(v5-M2): G2/G3 continuity + surface analysis + extension + N-side fill + advanced blend
CI / Build & Test (push) Failing after 35s
CI / Release Build (push) Failing after 1m32s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

M2.1 — G2/G3 连续性分析 (Agent #0):
- surface_continuity.h/.cpp: G0/G1/G2/G3 curve/surface detection
- Weingarten equation for curvature, Frénet frame, zebra stripe
- surface_analysis.h/.cpp: curvature_map, deviation_analysis, curvature_comb
- 24 tests (12 continuity + 12 analysis)

M2.2 — 曲面延伸 + N边填充 (Agent #1):
- surface_extension.h/.cpp: extend_surface(G1/G2), n_sided_fill, blend_surfaces
- Coons patch generalization for N-sided holes
- 16/16 tests passed in Docker container

M2.3 — 高级过渡曲面 (Agent #2):
- advanced_blend.h/.cpp: real implementations replacing stubs
- variable_radius_blend, multi_face_blend, rolling_ball_blend, face_face_blend
- Ball-rolling envelope + corner sphere filling
- 15+ tests with validate() verification
This commit is contained in:
茂之钳
2026-07-26 20:58:31 +08:00
parent 05b62e8238
commit 73df04d5cb
15 changed files with 4158 additions and 11 deletions
+122 -4
View File
@@ -1,8 +1,126 @@
#pragma once
/**
* @file advanced_blend.h
* @brief 高级过渡曲面(Advanced Blend)操作
*
* 提供多种超出简单恒定半径圆角的过渡曲面算法:
* - 沿边变半径过渡(variable_radius_blend
* - 多面同时过渡(multi_face_blend
* - 滚动球算法(rolling_ball_blend
* - 两面间恒定半径过渡(face_face_blend
*
* ## 与 fillet/fillet_variable 的关系
*
* modeling.h 中的 fillet 和 fillet_variable 是面向最终用户的高层 API,
* 本模块提供更底层的过渡曲面算法实现。两者可配合使用。
*
* @ingroup brep
*/
#include "vde/brep/brep.h"
#include <vector>
namespace vde::brep {
[[nodiscard]] BrepModel variable_radius_blend(const BrepModel& body, int edge_id, double r_start, double r_end);
[[nodiscard]] BrepModel multi_face_blend(const BrepModel& body, const std::vector<int>& face_ids, double radius);
[[nodiscard]] BrepModel rolling_ball_blend(const BrepModel& body, int edge_id, double radius);
} // namespace vde::brep
/**
* @brief 沿边变半径过渡
*
* 沿指定边的参数方向,在 r_start 和 r_end 之间线性插值半径,
* 每段独立构建过渡曲面片段,最终拼接为完整过渡。
*
* 算法:
* 1. 定位目标边及其两个邻面
* 2. 计算两面法向量及角平分线方向
* 3. 沿边曲线采样 N 个截面,每个截面处计算半径和切点
* 4. 每相邻两截面间构建一张过渡曲面(NURBS quad patch
* 5. 重建两个邻面,切除过渡区域
* 6. 复制未受影响的面,组装壳和体
*
* @param body 输入实体
* @param edge_id 目标边索引
* @param r_start 边起点处的过渡半径(≥ 0)
* @param r_end 边终点处的过渡半径(≥ 0)
* @param samples 沿边的截面采样数(默认 16)
* @return 过渡后的新实体
*
* @pre edge_id 有效,r_start ≥ 0, r_end ≥ 0
* @note samples 越大截面过渡越光滑,但面和边数增加
*/
[[nodiscard]] BrepModel variable_radius_blend(
const BrepModel& body, int edge_id,
double r_start, double r_end, int samples = 16);
/**
* @brief 多面同时过渡
*
* 对多个面同时施加恒定半径的过渡处理。每个目标面独立向内偏移,
* 目标面之间的交角处构造球面填充,非目标面保持不变。
*
* 算法:
* 1. 对每个目标面,沿面法向量向内偏移半径距离
* 2. 检测目标面之间的共享边,在交线处构造圆柱过渡
* 3. 检测三面以上交角顶点,用球面贴片填充
* 4. 复制非目标面,组装壳和体
*
* @param body 输入实体
* @param face_ids 目标面索引列表
* @param radius 过渡半径(> 0
* @return 过渡后的新实体
*
* @pre face_ids 非空,所有索引有效,radius > 0
*/
[[nodiscard]] BrepModel multi_face_blend(
const BrepModel& body, const std::vector<int>& face_ids, double radius);
/**
* @brief 滚动球算法过渡
*
* 模拟一个半径为 radius 的球体沿目标边滚动,球体的扫掠包络面即为过渡曲面。
* 球体始终保持与边的两个邻面接触,其中心沿边的等距曲线移动。
*
* 算法:
* 1. 计算滚动球中心轨迹(沿边的 offset 曲线,offset = r / cos(half_angle)
* 2. 沿边采样球心位置
* 3. 每个球心位置计算球与两面的切点
* 4. 相邻截面间构建过渡曲面 patch(近似球体扫掠包络)
* 5. 重建邻面、复制非影响面、组装壳和体
*
* 对直边退化为圆柱过渡,对曲边产生自然的变曲率过渡。
*
* @param body 输入实体
* @param edge_id 目标边索引
* @param radius 球体半径(> 0
* @param samples 沿边的球心采样数(默认 20)
* @return 过渡后的新实体
*
* @pre edge_id 有效,radius > 0
*/
[[nodiscard]] BrepModel rolling_ball_blend(
const BrepModel& body, int edge_id,
double radius, int samples = 20);
/**
* @brief 两面间恒定半径过渡
*
* 在两个任意面之间构造恒定半径的过渡曲面。与单边过渡不同,
* 此函数处理两个面之间的整个共享或邻近区域。
*
* 算法:
* 1. 找到两面之间的共享边,或确定两面的邻近区域
* 2. 沿共享边界计算过渡曲面(恒定半径圆柱面)
* 3. 切掉两面在过渡区域的部分
* 4. 插入过渡曲面,组装壳和体
*
* @param body 输入实体
* @param face_a 第一个面索引
* @param face_b 第二个面索引
* @param radius 过渡半径(> 0
* @param samples 沿边界的截面采样数(默认 16)
* @return 过渡后的新实体
*
* @pre face_a 和 face_b 有效,radius > 0
*/
[[nodiscard]] BrepModel face_face_blend(
const BrepModel& body, int face_a, int face_b,
double radius, int samples = 16);
} // namespace vde::brep
+228
View File
@@ -0,0 +1,228 @@
#pragma once
/**
* @file surface_analysis.h
* @brief 曲面分析工具集 — 曲率映射、斑马纹、曲率梳、偏差分析、拔模角
*
* 提供 CAD 级曲面质量分析功能:
* - 高斯/平均/主曲率网格
* - 斑马纹反射模拟
* - 曲率梳可视化数据
* - 曲面间偏差分析
* - 拔模角计算(增强现有实现)
*
* @ingroup curves
*/
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include "vde/brep/brep.h"
#include <vector>
#include <array>
#include <utility>
#include <limits>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
// ═══════════════════════════════════════════════════════════
// Curvature Map Types
// ═══════════════════════════════════════════════════════════
/// 单点的曲率数据
struct CurvaturePoint {
double gaussian = 0.0; ///< 高斯曲率 K = κ₁·κ₂
double mean = 0.0; ///< 平均曲率 H = (κ₁+κ₂)/2
double k1 = 0.0; ///< 最大主曲率 κ₁
double k2 = 0.0; ///< 最小主曲率 κ₂
Vector3D d1; ///< 最大主方向
Vector3D d2; ///< 最小主方向
};
/// 曲率网格(res_u+1 × res_v+1
struct CurvatureMap {
int res_u, res_v; ///< 网格分辨率
std::vector<std::vector<CurvaturePoint>> grid; ///< grid[i][j] 对应 (u_i, v_j)
/// 高斯曲率极值
double min_gaussian = std::numeric_limits<double>::max();
double max_gaussian = -std::numeric_limits<double>::max();
/// 平均曲率极值
double min_mean = std::numeric_limits<double>::max();
double max_mean = -std::numeric_limits<double>::max();
/// 是否为可展曲面(高斯曲率 ≈ 0)
[[nodiscard]] bool is_developable(double tol = 1e-9) const;
};
/**
* @brief 计算 NURBS 曲面的曲率映射
*
* 在参数域均匀采样,计算每个采样点的高斯曲率、平均曲率、
* 主曲率及主方向。
*
* @param surf NURBS 曲面
* @param res_u u 方向分辨率(单元格数)
* @param res_v v 方向分辨率(单元格数)
* @return CurvatureMap
*/
[[nodiscard]] CurvatureMap curvature_map(
const NurbsSurface& surf,
int res_u, int res_v);
// ═══════════════════════════════════════════════════════════
// Zebra Stripe
// ═══════════════════════════════════════════════════════════
/// 斑马纹反射数据
struct ZebraStripeResult {
int res_u, res_v; ///< 网格分辨率
std::vector<std::vector<double>> intensity; ///< intensity[i][j] ∈ [0,1]
Vector3D light_direction; ///< 光照方向(归一化)
};
/**
* @brief 计算斑马纹反射数据(等照度线模拟)
*
* 模拟等间距平行光源在曲面上的反射效果,用于检测曲面光顺性。
* 斑马纹的密度和均匀度反映曲面的曲率变化。
*
* @param surf NURBS 曲面
* @param light_dir 光照方向(自动归一化)
* @param res_u u 方向分辨率
* @param res_v v 方向分辨率
* @return 反射强度网格
*/
[[nodiscard]] ZebraStripeResult zebra_stripe(
const NurbsSurface& surf,
const Vector3D& light_dir,
int res_u, int res_v);
// ═══════════════════════════════════════════════════════════
// Curvature Comb
// ═══════════════════════════════════════════════════════════
/// 曲率梳数据点
struct CurvatureCombPoint {
Point3D curve_point; ///< 曲线上的点
Vector3D curvature_vec; ///< 曲率向量(方向=主法线,长度∝曲率)
double curvature; ///< 曲率大小
};
/// 曲率梳完整数据
struct CurvatureComb {
std::vector<CurvatureCombPoint> points;
double min_curvature = std::numeric_limits<double>::max();
double max_curvature = -std::numeric_limits<double>::max();
double scale = 1.0; ///< 缩放因子
};
/**
* @brief 计算曲线曲率梳数据
*
* 在曲线上均匀采样,计算各点的曲率向量。
* 曲率梳将曲率大小可视化为从曲线向外延伸的线段长度。
*
* @param curve NURBS 曲线
* @param res 采样分辨率(点数)
* @param scale 曲率缩放因子(默认自动适配)
* @return 曲率梳数据
*/
[[nodiscard]] CurvatureComb curvature_comb(
const NurbsCurve& curve,
int res,
double scale = 0.0);
// ═══════════════════════════════════════════════════════════
// Deviation Analysis
// ═══════════════════════════════════════════════════════════
/// 偏差分析结果
struct DeviationResult {
double max_positive = 0.0; ///< 最大正偏差(surf_b 在 surf_a 外侧)
double max_negative = 0.0; ///< 最大负偏差(surf_b 在 surf_a 内侧)
double rms = 0.0; ///< 均方根偏差
double mean_absolute = 0.0; ///< 平均绝对偏差
size_t sample_count = 0; ///< 采样点数量
std::vector<double> deviations; ///< 各采样点的有符号偏差
std::vector<Point3D> sample_points_a; ///< surf_a 上的采样点
std::vector<Point3D> sample_points_b; ///< surf_b 上对应的最近点
};
/**
* @brief 计算两曲面的偏差分析
*
* 在 surf_a 参数域均匀采样,对每个采样点寻找 surf_b 上的最近点,
* 沿 surf_a 法线方向计算有符号偏差。
*
* @param surf_a 基准曲面
* @param surf_b 对比曲面
* @param samples 采样点数量(总点数 = samples×samples
* @return 偏差分析结果
*/
[[nodiscard]] DeviationResult deviation_analysis(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
int samples);
// ═══════════════════════════════════════════════════════════
// Draft Face Angle (Enhanced)
// ═══════════════════════════════════════════════════════════
/**
* @brief 计算 NURBS 曲面上指定参数点的拔模角
*
* 拔模角 = angle between surface normal and pull direction minus 90°
* 正值表示面沿拔模方向向外张开。
*
* @param surf NURBS 曲面
* @param u u 参数
* @param v v 参数
* @param pull_dir 拔模方向(自动归一化)
* @return 拔模角(弧度)
*/
[[nodiscard]] double draft_face_angle(
const NurbsSurface& surf,
double u, double v,
const Vector3D& pull_dir);
/**
* @brief 计算 B-Rep 面的拔模角(增强版)
*
* 在面的参数域中采样多个点,返回面积加权平均拔模角。
*
* @param body B-Rep 模型
* @param face_id 面 ID
* @param pull_dir 拔模方向
* @param samples 每方向采样点数(默认 10)
* @return 面积加权平均拔模角(弧度),若面无效则返回 0
*/
[[nodiscard]] double draft_face_angle(
const brep::BrepModel& body,
int face_id,
const Vector3D& pull_dir,
int samples = 10);
/**
* @brief 计算 B-Rep 面的拔模角分布
*
* 返回面上多个采样点的拔模角数据。
*/
struct DraftAngleDistribution {
double min_angle, max_angle; ///< 拔模角范围
double mean_angle; ///< 平均拔模角
double area_weighted_angle; ///< 面积加权平均
std::vector<double> sample_angles; ///< 各采样点拔模角
std::vector<std::pair<double,double>> sample_params; ///< 对应参数 (u,v)
};
[[nodiscard]] DraftAngleDistribution draft_face_angle_distribution(
const brep::BrepModel& body,
int face_id,
const Vector3D& pull_dir,
int samples = 15);
} // namespace vde::curves
+155
View File
@@ -0,0 +1,155 @@
#pragma once
/**
* @file surface_continuity.h
* @brief 曲面连续性分析 — G0/G1/G2/G3 连续性检测
*
* 提供曲线间和曲面间沿公共边的连续性分类。
*
* ### 连续性等级
*
* | 等级 | 条件 |
* |------|------|
* | G0 | 位置连续:两端点重合 |
* | G1 | 切向连续:一阶导数共线(方向一致) |
* | G2 | 曲率连续:曲率大小和方向匹配 |
* | G3 | 曲率变化率连续:曲率的变化率匹配 |
*
* @ingroup curves
*/
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include <vector>
#include <utility>
namespace vde::curves {
// ═══════════════════════════════════════════════════════════
// Continuity Level Enum
// ═══════════════════════════════════════════════════════════
/// 连续性等级
enum class ContinuityLevel {
None, ///< 不连续(甚至不满足 G0)
G0, ///< 位置连续
G1, ///< 切向连续
G2, ///< 曲率连续
G3, ///< 曲率变化率连续
};
/// 将 ContinuityLevel 转为可读字符串
[[nodiscard]] const char* to_string(ContinuityLevel level);
// ═══════════════════════════════════════════════════════════
// Curve Continuity
// ═══════════════════════════════════════════════════════════
/**
* @brief 分析两条 NURBS 曲线在公共参数点的连续性
*
* 在给定参数点计算两曲线的位置、一阶、二阶、三阶导数,
* 按 G0→G1→G2→G3 逐级判定。
*
* @param curve_a 第一条 NURBS 曲线
* @param t_a curve_a 上公共点的参数值
* @param curve_b 第二条 NURBS 曲线
* @param t_b curve_b 上公共点的参数值
* @param tol 位置容差(默认 1e-6)
* @return 满足的最高连续性等级
*
* @note G1 要求一阶导数共线(方向相同),G2 要求曲率向量一致,
* G3 要求曲率变化率一致。判定是累积的:G3 自动满足 G2/G1/G0。
*/
[[nodiscard]] ContinuityLevel continuity_type(
const NurbsCurve& curve_a, double t_a,
const NurbsCurve& curve_b, double t_b,
double tol = 1e-6);
/**
* @brief 重载:在两条曲线端点处检测连续性
*
* 自动探测 curve_a 和 curve_b 各两个端点,检查是否有重合端点对,
* 然后调用上述函数分析该连接点的连续性等级。
*
* @param curve_a 第一条曲线
* @param curve_b 第二条曲线
* @param tol 位置容差
* @return 若存在重合端点则返回连续性等级,否则返回 ContinuityLevel::None
*/
[[nodiscard]] ContinuityLevel continuity_type(
const NurbsCurve& curve_a,
const NurbsCurve& curve_b,
double tol = 1e-6);
// ═══════════════════════════════════════════════════════════
// Surface Continuity
// ═══════════════════════════════════════════════════════════
/**
* @brief 沿公共等参边分析两张 NURBS 曲面的连续性
*
* 在沿公共边的采样点处评估曲面连续性,返回最弱点等级。
* 例如 surf_a 的 u=u_max 边与 surf_b 的 u=u_min 边相邻。
*
* @param surf_a 第一张曲面
* @param surf_b 第二张曲面
* @param edge_param_range_a 公共边在 surf_a 上的参数范围 {{u, v_start}, {u, v_end}}
* @param edge_param_range_b 公共边在 surf_b 上的参数范围 {{u, v_start}, {u, v_end}}
* @param samples 沿边的采样点数量(默认 20)
* @param tol 位置容差(默认 1e-6)
* @return 所有采样点中的最低连续性等级
*
* @note 若曲面在采样点间连续但曲面方向(法线)相反,
* 仍判定为 G0(因为 G1 要求一阶导数方向一致)。
*/
[[nodiscard]] ContinuityLevel surface_continuity(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
std::pair<std::pair<double, double>, std::pair<double, double>> edge_param_range_a,
std::pair<std::pair<double, double>, std::pair<double, double>> edge_param_range_b,
int samples = 20,
double tol = 1e-6);
/**
* @brief 沿公共边自动检测并分析两张曲面连续性
*
* 自动检测 surf_a 和 surf_b 的公共边(通过比较四条等参边界),
* 然后调用 surface_continuity 分析。
*
* @param surf_a 第一张曲面
* @param surf_b 第二张曲面
* @param samples 采样点数量
* @param tol 位置容差
* @return 所有公共边中的最低连续性等级(若无公共边则返回 None)
*/
[[nodiscard]] ContinuityLevel surface_continuity(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
int samples = 20,
double tol = 1e-6);
/**
* @brief 曲面间连续性详细报告
*
* 返回每个采样点的连续性等级向量,便于诊断。
*/
struct SurfaceContinuityReport {
ContinuityLevel worst; ///< 最弱等级
ContinuityLevel best; ///< 最佳等级
double g1_ratio; ///< G1 通过率 (0~1)
double g2_ratio; ///< G2 通过率 (0~1)
std::vector<ContinuityLevel> per_sample; ///< 各采样点等级
std::vector<double> position_errors; ///< 各采样点位置误差
};
/**
* @brief 获取曲面连续性详细报告
*/
[[nodiscard]] SurfaceContinuityReport surface_continuity_report(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
int samples = 50,
double tol = 1e-6);
} // namespace vde::curves
+116
View File
@@ -0,0 +1,116 @@
#pragma once
#include "vde/curves/nurbs_surface.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/core/point.h"
#include <vector>
#include <utility>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
/**
* @brief 连续性级别
* @ingroup curves
*/
enum class Continuity {
C0 = 0, ///< 仅位置连续
G1 = 1, ///< 切线/切平面连续
G2 = 2, ///< 曲率连续
};
/**
* @brief 曲面延伸
*
* 沿等参边界延伸曲面,支持 G1 切平面连续。
* 对于 G2 连续,在约束方向构造 3 排控制点以保持曲率。
*
* @param surface 输入 NURBS 曲面
* @param edge_id 边界边索引(0=umin, 1=umax, 2=vmin, 3=vmax
* @param distance 延伸距离(参数空间)
* @param continuity 期望的连续性级别(默认 G1)
* @return 延伸后的 NURBS 曲面
*
* @note G1 模式:追加 1 排控制点,沿切向延伸
* @note G2 模式:追加 2 排控制点,第 2 排由曲率约束计算
* @note 延伸仅沿切向线性/二次外推,不保证与原曲面精确 G2 连续
*
* @code{.cpp}
* auto ext = extend_surface(surf, 1, 0.5, Continuity::G1);
* @endcode
* @ingroup curves
*/
[[nodiscard]] NurbsSurface extend_surface(const NurbsSurface& surface,
int edge_id, double distance,
Continuity continuity = Continuity::G1);
/**
* @brief N 边孔洞填充
*
* 用 N 条边界曲线构造 Coons patch 泛化的填充曲面。
* 支持三角形(N=3)、四边形(N=4)、五边形(N=5)等任意边数。
*
* 算法:将边界曲线映射到单位圆参数域,使用径向基函数插值
* 构造中心点,再以双线性 Coons 方式生成内部控制点。
*
* @param boundary_curves N 条边界曲线,需围成闭合环(首尾相连)
* @param continuity 期望的连续性级别(默认 G2)
* @return 填充 NURBS 曲面
*
* @note G1 模式:单层中间环,线性插值
* @note G2 模式:双层中间环,保持曲率衰减
* @note 至少需要 3 条边界曲线
*
* @code{.cpp}
* auto fill = n_sided_fill({c1, c2, c3}, Continuity::G2); // 三角形孔
* @endcode
* @ingroup curves
*/
[[nodiscard]] NurbsSurface n_sided_fill(const std::vector<NurbsCurve>& boundary_curves,
Continuity continuity = Continuity::G2);
/**
* @brief 两面间过渡曲面
*
* 检测两面公共边界,沿该边构造圆弧或 S 形过渡曲面。
* 与 nurbs_operations 中的 blend_surfaces 不同,此版本自动检测公共边。
*
* 算法:
* 1. 检测 surf_a 和 surf_b 的共享边界
* 2. 沿共享边界两侧各偏移 radius 提取曲线
* 3. 在两条偏移曲线间构造过渡曲面(直纹面或 Coons 面)
*
* @param surf_a 第一个曲面
* @param surf_b 第二个曲面
* @param radius 过渡半径
* @return 过渡 NURBS 曲面
*
* @note 如果两面无公共边界,返回退化的空曲面
* @note 过渡面在边界处与原曲面 G1 连续(等参方向)
*
* @code{.cpp}
* auto blend = blend_surfaces(surf_a, surf_b, 0.3);
* @endcode
* @ingroup curves
*/
[[nodiscard]] NurbsSurface blend_surfaces(const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
double radius);
/**
* @brief 检查两曲面沿共享边界的 G1 连续性
*
* @param surf_a 第一个曲面
* @param surf_b 第二个曲面
* @param edge surf_a 的边界边索引(0=umin, 1=umax, 2=vmin, 3=vmax
* @param samples 沿边界的采样点数(默认 10)
* @return 所有采样点均满足 G1 连续时返回 true
*
* @ingroup curves
*/
[[nodiscard]] bool is_g1_continuous(const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
int edge, int samples = 10);
} // namespace vde::curves