docs: doxygen annotations for curves + mesh + sketch
This commit is contained in:
@@ -6,24 +6,88 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief 任意阶 Bézier 曲线
|
||||
*
|
||||
* 通过 de Casteljau 递推算法定义的参数多项式曲线。
|
||||
* 控制点 cp_ 的数量 = p+1(p 为次数)。
|
||||
* 参数域 t ∈ [0, 1],端点插值首末控制点。
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class BezierCurve {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 Bézier 曲线
|
||||
* @param control_points 控制点序列,点数 = degree + 1,最少 2 个
|
||||
* @note 控制点数量决定了曲线阶次;点数越多,曲线越灵活但计算越昂贵
|
||||
* @code{.cpp}
|
||||
* BezierCurve curve({p0, p1, p2, p3}); // 三次 Bézier
|
||||
* @endcode
|
||||
*/
|
||||
explicit BezierCurve(std::vector<Point3D> control_points);
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求值曲线点
|
||||
* @param t 参数值,范围 [0, 1]
|
||||
* @return 曲线上对应 t 的点坐标
|
||||
* @note 使用 de Casteljau 递推算法,O(n²) 复杂度
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double t) const;
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求导数
|
||||
* @param t 参数值,范围 [0, 1]
|
||||
* @param order 导数阶数,1 = 一阶(切向量),2 = 二阶(曲率相关)
|
||||
* @return 导数向量
|
||||
* @note 高阶导数通过降阶 Bézier 曲线计算:
|
||||
* P^{(k)}(t) = n(n-1)...(n-k+1) Σ Δ^k P_i · B_i^{n-k}(t)
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative(double t, int order = 1) const;
|
||||
|
||||
/**
|
||||
* @brief 曲线阶次
|
||||
* @return 阶次 = 控制点数 - 1
|
||||
*/
|
||||
[[nodiscard]] int degree() const { return static_cast<int>(cp_.size()) - 1; }
|
||||
|
||||
/**
|
||||
* @brief 参数定义域
|
||||
* @return 固定为 [0, 1]
|
||||
*/
|
||||
[[nodiscard]] std::pair<double, double> domain() const { return {0.0, 1.0}; }
|
||||
|
||||
/**
|
||||
* @brief 控制点访问
|
||||
* @return 控制点向量的只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<Point3D>& control_points() const { return cp_; }
|
||||
|
||||
/// Split at parameter t into two curves
|
||||
/**
|
||||
* @brief 在 t 处将曲线一分为二
|
||||
* @param t 分割参数,范围 [0, 1]
|
||||
* @return 左段 + 右段两条 Bézier 曲线
|
||||
* @note 使用 de Casteljau 分割算法,分割点为三角阵的对角线
|
||||
* @code{.cpp}
|
||||
* auto [left, right] = curve.split(0.5);
|
||||
* // left: 控制点 = [P0, b1, b2, P(t)]
|
||||
* // right: 控制点 = [P(t), b3, b4, P3]
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::pair<BezierCurve, BezierCurve> split(double t) const;
|
||||
|
||||
/// Degree elevation
|
||||
/**
|
||||
* @brief 升阶操作
|
||||
* @param times 升阶次数,≥ 1
|
||||
* @return 升阶后的 Bézier 曲线(控制点数增加 times,形状不变)
|
||||
* @note 通过反复应用公式 P_i' = i/(n+1) P_{i-1} + (1 - i/(n+1)) P_i
|
||||
* 升阶不仅增加自由度,也用于不同阶曲线间的兼容操作
|
||||
* @see split
|
||||
*/
|
||||
[[nodiscard]] BezierCurve degree_elevate(int times = 1) const;
|
||||
|
||||
private:
|
||||
std::vector<Point3D> cp_;
|
||||
std::vector<Point3D> cp_; ///< 控制点序列
|
||||
};
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -6,20 +6,85 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief Bézier 张量积曲面
|
||||
*
|
||||
* 由 (m+1)×(n+1) 控制点网格定义,m = degree_u,n = degree_v。
|
||||
* 参数域 (u,v) ∈ [0,1]×[0,1],四个角点插值网格四角。
|
||||
*
|
||||
* 求值公式:S(u,v) = Σ_i Σ_j P_{ij} · B_i^m(u) · B_j^n(v)
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class BezierSurface {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 Bézier 曲面
|
||||
* @param control_grid 控制点网格,grid[i][j] 对应 u 方向第 i 个、v 方向第 j 个控制点
|
||||
* @note grid 必须矩整(所有行等长);grid.size() = degree_u+1, grid[0].size() = degree_v+1
|
||||
* @code{.cpp}
|
||||
* // 3x3 控制网格 → 双二次 Bézier 曲面
|
||||
* BezierSurface surf({{p00,p01,p02}, {p10,p11,p12}, {p20,p21,p22}});
|
||||
* @endcode
|
||||
*/
|
||||
BezierSurface(std::vector<std::vector<Point3D>> control_grid);
|
||||
|
||||
/**
|
||||
* @brief 求曲面点 S(u,v)
|
||||
* @param u u 参数,范围 [0, 1]
|
||||
* @param v v 参数,范围 [0, 1]
|
||||
* @return 曲面上对应 (u,v) 的 3D 坐标
|
||||
* @note 张量积求值:先按 v 方向对每行做 de Casteljau,再沿 u 方向对结果做一次
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 方向偏导数 ∂S/∂u
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return u 方向切向量
|
||||
* @note 通过降阶 Bézier 曲线计算:对 v 求值后沿 u 求导
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_u(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief v 方向偏导数 ∂S/∂v
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return v 方向切向量
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_v(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief 单位法向量 N(u,v) = (∂S/∂u × ∂S/∂v) / |…|
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return 归一化法向量(指向曲面的特选侧)
|
||||
* @note 当两个偏导数平行时返回零向量(奇异点)
|
||||
*/
|
||||
[[nodiscard]] Vector3D normal(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 向阶次
|
||||
* @return u 方向阶次(控制点行数 - 1)
|
||||
*/
|
||||
[[nodiscard]] int degree_u() const { return static_cast<int>(cp_.size()) - 1; }
|
||||
|
||||
/**
|
||||
* @brief v 向阶次
|
||||
* @return v 方向阶次(控制点列数 - 1)
|
||||
*/
|
||||
[[nodiscard]] int degree_v() const { return static_cast<int>(cp_[0].size()) - 1; }
|
||||
|
||||
private:
|
||||
std::vector<std::vector<Point3D>> cp_;
|
||||
std::vector<std::vector<Point3D>> cp_; ///< 控制点网格 [u_idx][v_idx]
|
||||
|
||||
/**
|
||||
* @brief 一维 de Casteljau 求值(内部辅助)
|
||||
* @param t 参数
|
||||
* @param pts 一维控制点序列
|
||||
* @return 求值结果
|
||||
*/
|
||||
[[nodiscard]] Point3D de_casteljau(double t, const std::vector<Point3D>& pts) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,32 +6,117 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief B 样条曲线(非有理、均匀/非均匀节点)
|
||||
*
|
||||
* 由 n+1 个控制点、m+1 个节点向量和阶次 p 定义。
|
||||
* 节点向量必须满足 m = n + p + 1。
|
||||
* 参数域为 [knots[p], knots[n+1]],由前 p 个和后 p 个节点修剪。
|
||||
*
|
||||
* 与 Bézier 的主要区别:
|
||||
* - 局部控制:修改单个控制点仅影响 [t_i, t_{i+p+1}] 区间
|
||||
* - 节点插入可引入更多控制点而不改变形状
|
||||
* - 通过多重节点可达 C^{p-k} 连续性
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class BSplineCurve {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 B 样条曲线
|
||||
* @param control_points n+1 个控制点
|
||||
* @param knots 节点向量,必须为非递减序列
|
||||
* @param degree 阶次 p
|
||||
* @note 节点数量 = 控制点数 + 阶次 + 1;clamped 端点多重度为 p+1
|
||||
* @code{.cpp}
|
||||
* // 三次 B 样条,7 个控制点
|
||||
* BSplineCurve curve(cps, {0,0,0,0, 0.25,0.5,0.75, 1,1,1,1}, 3);
|
||||
* @endcode
|
||||
*/
|
||||
BSplineCurve(std::vector<Point3D> control_points,
|
||||
std::vector<double> knots, int degree);
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求值曲线点
|
||||
* @param t 参数值,必须在 domain() 范围内
|
||||
* @return 曲线上对应 t 的点坐标
|
||||
* @note 使用 de Boor(Cox-de Boor)递推算法
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double t) const;
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求导数
|
||||
* @param t 参数值
|
||||
* @param order 导数阶数,1 = 一切向量
|
||||
* @return 导数向量
|
||||
* @note B 样条导数公式:
|
||||
* P^{(k)}(t) = Σ P_i^{(k)} N_{i,p-k}(t),其中
|
||||
* P_i^{(0)} = P_i,
|
||||
* P_i^{(k)} = p·(P_i^{(k-1)} - P_{i-1}^{(k-1)}) / (t_{i+p-k+1} - t_i)
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative(double t, int order = 1) const;
|
||||
|
||||
/**
|
||||
* @brief 曲线阶次
|
||||
* @return 阶次 p
|
||||
*/
|
||||
[[nodiscard]] int degree() const { return degree_; }
|
||||
|
||||
/**
|
||||
* @brief 参数定义域
|
||||
* @return 有效参数范围 [knots[p], knots[n+1]]
|
||||
*/
|
||||
[[nodiscard]] std::pair<double, double> domain() const;
|
||||
|
||||
/**
|
||||
* @brief 控制点访问
|
||||
* @return 控制点数组只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<Point3D>& control_points() const { return cp_; }
|
||||
|
||||
/**
|
||||
* @brief 节点向量访问
|
||||
* @return 节点向量只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots() const { return knots_; }
|
||||
|
||||
/// Find knot span index for t
|
||||
/**
|
||||
* @brief 查找 t 所在的节点区间
|
||||
* @param t 参数值
|
||||
* @return 节点区间索引 i 满足 knots[i] ≤ t < knots[i+1]
|
||||
* @note 使用二分查找,O(log n);求值和基函数的前置步骤
|
||||
* @code{.cpp}
|
||||
* int span = curve.find_span(0.5);
|
||||
* auto N = curve.basis_functions(0.5, span);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] int find_span(double t) const;
|
||||
|
||||
/// Evaluate basis functions at t
|
||||
/**
|
||||
* @brief 在 t 处求非零基函数值
|
||||
* @param t 参数值
|
||||
* @param span 节点区间索引,-1 表示自动查找
|
||||
* @return N_{span-p}(t), ..., N_{span}(t) 共 p+1 个值
|
||||
* @note 仅返回 p+1 个非零基函数;用于端点求值及插值
|
||||
* @see find_span
|
||||
*/
|
||||
[[nodiscard]] std::vector<double> basis_functions(double t, int span = -1) const;
|
||||
|
||||
/// Evaluate first derivatives of non-zero basis functions at t
|
||||
/// Returns dN_{span-degree+i, degree}/du for i = 0..degree
|
||||
/**
|
||||
* @brief 在 t 处求非零基函数的一阶导数
|
||||
* @param t 参数值
|
||||
* @param span 节点区间索引,-1 表示自动查找
|
||||
* @return dN_{span-p}/du, ..., dN_{span}/du 共 p+1 个值
|
||||
* @note B 样条基函数导数递推:
|
||||
* N'_{i,p} = p/(t_{i+p}-t_i)·N_{i,p-1} - p/(t_{i+p+1}-t_{i+1})·N_{i+1,p-1}
|
||||
* @see basis_functions
|
||||
*/
|
||||
[[nodiscard]] std::vector<double> basis_derivatives(double t, int span = -1) const;
|
||||
|
||||
private:
|
||||
std::vector<Point3D> cp_;
|
||||
std::vector<double> knots_;
|
||||
int degree_;
|
||||
std::vector<Point3D> cp_; ///< 控制点序列
|
||||
std::vector<double> knots_; ///< 节点向量(非递减)
|
||||
int degree_; ///< 曲线阶次
|
||||
};
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -7,26 +7,99 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief B 样条张量积曲面(非有理)
|
||||
*
|
||||
* 由 (nu+1)×(nv+1) 控制点网格、u/v 向各一个节点向量以及两个阶次定义。
|
||||
* 参数域 (u,v) ∈ [knots_u[pu], knots_u[nu+1]] × [knots_v[pv], knots_v[nv+1]]。
|
||||
*
|
||||
* 求值公式:S(u,v) = Σ_i Σ_j P_{ij} · N_{i,pu}(u) · N_{j,pv}(v)
|
||||
*
|
||||
* 具有局部控制性:修改单个控制点仅影响相邻 pu×pv 个节点区间的曲面区域。
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class BSplineSurface {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 B 样条曲面
|
||||
* @param control_grid (nu+1)×(nv+1) 控制点网格
|
||||
* @param knots_u u 向节点向量,长度 = nu + pu + 1
|
||||
* @param knots_v v 向节点向量,长度 = nv + pv + 1
|
||||
* @param degree_u u 向阶次 pu
|
||||
* @param degree_v v 向阶次 pv
|
||||
* @code{.cpp}
|
||||
* BSplineSurface surf(grid,
|
||||
* {0,0,0,0, 0.5, 1,1,1,1}, // u knots (三次,5 个控制点)
|
||||
* {0,0,0, 0.5, 1,1,1}, // v knots (二次,4 个控制点)
|
||||
* 3, 2);
|
||||
* @endcode
|
||||
*/
|
||||
BSplineSurface(std::vector<std::vector<Point3D>> control_grid,
|
||||
std::vector<double> knots_u, std::vector<double> knots_v,
|
||||
int degree_u, int degree_v);
|
||||
|
||||
/**
|
||||
* @brief 求曲面点 S(u,v)
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return 曲面上的 3D 点
|
||||
* @note 张量积分解:先行后列(或先列后行)做两次 B 样条求值
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 向偏导数 ∂S/∂u
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return u 向切向量
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_u(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief v 向偏导数 ∂S/∂v
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return v 向切向量
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_v(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief 单位法向量
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return N(u,v) = (∂S/∂u × ∂S/∂v) / |…|
|
||||
*/
|
||||
[[nodiscard]] Vector3D normal(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 向阶次
|
||||
* @return pu
|
||||
*/
|
||||
[[nodiscard]] int degree_u() const { return degree_u_; }
|
||||
|
||||
/**
|
||||
* @brief v 向阶次
|
||||
* @return pv
|
||||
*/
|
||||
[[nodiscard]] int degree_v() const { return degree_v_; }
|
||||
|
||||
/**
|
||||
* @brief u 向节点向量
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots_u() const { return knots_u_; }
|
||||
|
||||
/**
|
||||
* @brief v 向节点向量
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots_v() const { return knots_v_; }
|
||||
|
||||
private:
|
||||
std::vector<std::vector<Point3D>> cp_;
|
||||
std::vector<double> knots_u_, knots_v_;
|
||||
int degree_u_, degree_v_;
|
||||
std::vector<std::vector<Point3D>> cp_; ///< 控制点网格
|
||||
std::vector<double> knots_u_, knots_v_; ///< u/v 向节点向量
|
||||
int degree_u_, degree_v_; ///< u/v 向阶次
|
||||
};
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -7,24 +7,94 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief NURBS 曲线(Non-Uniform Rational B-Spline)
|
||||
*
|
||||
* B 样条的有理推广,引入权重 w_i 使每个控制点的影响可调。
|
||||
* 通过投影变换将齐次坐标中的 B 样条映射到 3D 空间。
|
||||
*
|
||||
* 求值公式:C(t) = Σ N_{i,p}(t) · w_i · P_i / Σ N_{i,p}(t) · w_i
|
||||
*
|
||||
* 关键性质:
|
||||
* - 可精确表示圆锥曲线(圆、椭圆)——Bézier/B 样条无法做到
|
||||
* - 权重 w_i 越大,曲线越靠近控制点 P_i
|
||||
* - 所有权重 = 1 时退化为 B 样条
|
||||
* - 所有权重 > 0 时在凸包内
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class NurbsCurve {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 NURBS 曲线
|
||||
* @param control_points 控制点序列
|
||||
* @param knots 节点向量
|
||||
* @param weights 权重序列,必须与 control_points 等长,各分量 > 0
|
||||
* @param degree 阶次 p
|
||||
* @note 所有权重必须 > 0 以保证分母不为零(非退化)
|
||||
* @code{.cpp}
|
||||
* // 用 NURBS 表示四分之一圆(7 个控制点,三次)
|
||||
* NurbsCurve arc(cps, {0,0,0,0, 0.5, 1,1,1,1},
|
||||
* {1, sqrt2/2, 1, sqrt2/2, 1, sqrt2/2, 1}, 3);
|
||||
* @endcode
|
||||
*/
|
||||
NurbsCurve(std::vector<Point3D> control_points, std::vector<double> knots,
|
||||
std::vector<double> weights, int degree);
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求值
|
||||
* @param t 参数值
|
||||
* @return 曲线上对应 t 的点坐标
|
||||
* @note 内部将 (w·P, w) 提升为齐次坐标的 B 样条,求值后投影回 3D
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double t) const;
|
||||
|
||||
/**
|
||||
* @brief 在参数 t 处求导数
|
||||
* @param t 参数值
|
||||
* @param order 导数阶数
|
||||
* @return 导数向量
|
||||
* @note 有理导数公式:
|
||||
* C^{(k)} = (A^{(k)} - Σ_{j=1}^{k} C(k,j) · w^{(j)} · C^{(k-j)}) / w
|
||||
* 其中 A(t) = Σ N_i(t)·w_i·P_i,w(t) = Σ N_i(t)·w_i
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative(double t, int order = 1) const;
|
||||
|
||||
/**
|
||||
* @brief 曲线阶次
|
||||
* @return p
|
||||
*/
|
||||
[[nodiscard]] int degree() const { return degree_; }
|
||||
|
||||
/**
|
||||
* @brief 参数定义域
|
||||
* @return [knots[degree], knots[n+1]]
|
||||
*/
|
||||
[[nodiscard]] std::pair<double, double> domain() const;
|
||||
|
||||
/**
|
||||
* @brief 控制点访问
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<Point3D>& control_points() const { return cp_; }
|
||||
|
||||
/**
|
||||
* @brief 权重访问
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& weights() const { return weights_; }
|
||||
|
||||
/**
|
||||
* @brief 节点向量访问
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots() const { return knots_; }
|
||||
|
||||
private:
|
||||
std::vector<Point3D> cp_;
|
||||
std::vector<double> knots_;
|
||||
std::vector<double> weights_;
|
||||
int degree_;
|
||||
std::vector<Point3D> cp_; ///< 控制点
|
||||
std::vector<double> knots_; ///< 节点向量
|
||||
std::vector<double> weights_; ///< 权重,w_i > 0
|
||||
int degree_; ///< 阶次
|
||||
};
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -9,79 +9,162 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/// Offset a NURBS surface by a constant distance along its normal.
|
||||
/// Computes the normal at each control point's Greville abscissa and
|
||||
/// offsets the control point along that normal (Piegl & Tiller approximation).
|
||||
/// @param surf Input NURBS surface
|
||||
/// @param distance Offset distance (positive = outward along normal)
|
||||
/// @return Offset NURBS surface (approximation)
|
||||
/**
|
||||
* @brief NURBS 曲面等距偏移
|
||||
*
|
||||
* 基于 Piegl & Tiller 近似方法:在每个控制点的 Greville 横坐标处
|
||||
* 计算曲面法向,将控制点沿法向平移 distance。
|
||||
*
|
||||
* @param surf 输入 NURBS 曲面
|
||||
* @param distance 偏移距离(正值 = 沿法向外,负值 = 向内)
|
||||
* @return 偏移后的 NURBS 曲面(近似,阶次与原曲面相同)
|
||||
*
|
||||
* @note 这是几何近似,非精确偏移。对于大变曲率曲面,增大控制点数量
|
||||
* 可提高精度。精确偏移需要更高阶曲面或细分方法。
|
||||
* @note 偏移可能导致自交(distance 超过曲面局部曲率半径时)
|
||||
* @see trim_surface blend_surfaces
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface offset_surface(const NurbsSurface& surf, double distance);
|
||||
|
||||
/// Trim a NURBS surface to a parameter sub-domain via reparameterization.
|
||||
/// Maps the old domain [u_min,u_max]×[v_min,v_max] to [0,1]×[0,1] by
|
||||
/// shifting and scaling knot vectors. Control points and weights are unchanged;
|
||||
/// the surface geometry is preserved, only the parameter range changes.
|
||||
/// @param surf Input surface
|
||||
/// @param u_min, u_max Trimmed u range (must be within original domain)
|
||||
/// @param v_min, v_max Trimmed v range (must be within original domain)
|
||||
/// @return Trimmed NURBS surface
|
||||
/**
|
||||
* @brief NURBS 曲面参数域裁剪
|
||||
*
|
||||
* 将原始定义域 [u_min,u_max]×[v_min,v_max] 通过节点重参数化
|
||||
* 映射到 [0,1]×[0,1],不改变控制点和权重——仅缩放平移节点向量,
|
||||
* 曲面几何保持不变。
|
||||
*
|
||||
* @param surf 输入曲面
|
||||
* @param u_min,u_max 裁剪后的 u 参数范围(必须在原 domain 内)
|
||||
* @param v_min,v_max 裁剪后的 v 参数范围(必须在原 domain 内)
|
||||
* @return 裁剪后的 NURBS 曲面
|
||||
*
|
||||
* @note 裁剪是"软裁剪"——几何不变,仅改变参数化。如需"硬裁剪"
|
||||
* (沿等参线切除曲面边缘),需结合曲面细分。
|
||||
* @code{.cpp}
|
||||
* auto trimmed = trim_surface(surf, 0.2, 0.8, 0.1, 0.9);
|
||||
* // trimmed 的参数域为 [0,1]×[0,1],但几何仅对应原曲面的子区域
|
||||
* @endcode
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface trim_surface(const NurbsSurface& surf,
|
||||
double u_min, double u_max,
|
||||
double v_min, double v_max);
|
||||
|
||||
/// Blend between two surfaces along their boundary edges.
|
||||
/// Extracts boundary curves, offsets them inward by blend_radius along
|
||||
/// the surface normal, and creates a ruled (lofted) surface between them.
|
||||
/// @param surf_a First surface
|
||||
/// @param surf_b Second surface
|
||||
/// @param edge_a Edge of surf_a to blend from (0=umin, 1=umax, 2=vmin, 3=vmax)
|
||||
/// @param edge_b Edge of surf_b to blend to (0=umin, 1=umax, 2=vmin, 3=vmax)
|
||||
/// @param blend_radius Radius of the blend transition
|
||||
/// @return Blend surface (ruled surface between offset boundary curves)
|
||||
/**
|
||||
* @brief 两 NURBS 曲面沿边界边的过渡面
|
||||
*
|
||||
* 提取两个曲面的指定边界曲线,各沿法向向内偏移 blend_radius 后,
|
||||
* 在两偏移曲线之间创建直纹面作为过渡。
|
||||
*
|
||||
* @param surf_a 第一个曲面
|
||||
* @param surf_b 第二个曲面
|
||||
* @param edge_a surf_a 的边界边索引(0=u_min, 1=u_max, 2=v_min, 3=v_max)
|
||||
* @param edge_b surf_b 的边界边索引(0=u_min, 1=u_max, 2=v_min, 3=v_max)
|
||||
* @param blend_radius 过渡半径
|
||||
* @return 过渡 NURBS 曲面(在偏移边界曲线间的直纹面)
|
||||
*
|
||||
* @note 过渡面与原始曲面在边界处为 C⁰ 连续(非切线连续)
|
||||
* @code{.cpp}
|
||||
* // surf_a 的 u_max 边过渡到 surf_b 的 u_min 边
|
||||
* auto blend = blend_surfaces(surf_a, surf_b,
|
||||
* 1, 0, // edge indices: umax → umin
|
||||
* 0.5); // blend radius
|
||||
* @endcode
|
||||
* @see ruled_surface
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface blend_surfaces(const NurbsSurface& surf_a,
|
||||
const NurbsSurface& surf_b,
|
||||
int edge_a, int edge_b,
|
||||
double blend_radius);
|
||||
|
||||
/// Create a ruled surface between two NURBS curves.
|
||||
/// The surface is linear (degree=1) in the ruling direction and inherits
|
||||
/// the curve degree in the longitudinal direction.
|
||||
/// @param curve_a First curve (v=0 edge of ruled surface)
|
||||
/// @param curve_b Second curve (v=1 edge of ruled surface)
|
||||
/// @return Ruled NURBS surface
|
||||
/**
|
||||
* @brief 两 NURBS 曲线间的直纹面
|
||||
*
|
||||
* 在两条曲线之间线性插值生成曲面:
|
||||
* S(u,v) = (1-v)·C_a(u) + v·C_b(u)
|
||||
* 纵向保持输入曲线的阶次,直纹方向为线性(degree=1)。
|
||||
*
|
||||
* @param curve_a 第一条曲线(v=0 边界)
|
||||
* @param curve_b 第二条曲线(v=1 边界)
|
||||
* @return 直纹 NURBS 曲面
|
||||
*
|
||||
* @note 两条曲线需有兼容的阶次和节点结构(内部执行升阶和节点精化)
|
||||
* @code{.cpp}
|
||||
* auto ruled = ruled_surface(profile_bottom, profile_top);
|
||||
* @endcode
|
||||
* @see coons_patch blend_surfaces
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface ruled_surface(const NurbsCurve& curve_a,
|
||||
const NurbsCurve& curve_b);
|
||||
|
||||
/// Create a Coons patch from four boundary curves.
|
||||
/// Bilinear interpolation: S(u,v) = (1-u)*C_v0(v) + u*C_v1(v)
|
||||
/// + (1-v)*C_u0(u) + v*C_u1(u)
|
||||
/// - corner correction terms.
|
||||
/// All four curves should have compatible degree and knot structure.
|
||||
/// @param curve_u0 Boundary at v=0 (runs along u)
|
||||
/// @param curve_u1 Boundary at v=1 (runs along u)
|
||||
/// @param curve_v0 Boundary at u=0 (runs along v)
|
||||
/// @param curve_v1 Boundary at u=1 (runs along v)
|
||||
/// @return Coons patch NURBS surface
|
||||
/**
|
||||
* @brief 由四条边界曲线创建 Coons 曲面
|
||||
*
|
||||
* 双线性混合:
|
||||
* S(u,v) = (1-u)·C_{v0}(v) + u·C_{v1}(v)
|
||||
* + (1-v)·C_{u0}(u) + v·C_{u1}(u)
|
||||
* - 角点修正项
|
||||
*
|
||||
* 四条曲线须围成闭合环且有兼容的阶次/节点结构。
|
||||
*
|
||||
* @param curve_u0 v=0 处边界(沿 u 方向)
|
||||
* @param curve_u1 v=1 处边界(沿 u 方向)
|
||||
* @param curve_v0 u=0 处边界(沿 v 方向)
|
||||
* @param curve_v1 u=1 处边界(沿 v 方向)
|
||||
* @return Coons 曲面
|
||||
*
|
||||
* @note 仅保证边界插值,不保证内部形状与设计意图一致
|
||||
* @code{.cpp}
|
||||
* auto patch = coons_patch(bottom, top, left, right);
|
||||
* @endcode
|
||||
* @see ruled_surface
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface coons_patch(const NurbsCurve& curve_u0,
|
||||
const NurbsCurve& curve_u1,
|
||||
const NurbsCurve& curve_v0,
|
||||
const NurbsCurve& curve_v1);
|
||||
|
||||
/// Extrude a NURBS curve along a direction vector.
|
||||
/// Creates a degree(d)×1 NURBS surface where d is the curve degree.
|
||||
/// First row of control points = curve CPs, last row = offset CPs.
|
||||
/// @param curve Profile curve
|
||||
/// @param direction Extrusion direction (will be normalized internally)
|
||||
/// @param length Extrusion length
|
||||
/// @return Extruded NURBS surface
|
||||
/**
|
||||
* @brief 沿方向向量拉伸 NURBS 曲线为曲面
|
||||
*
|
||||
* 创建阶次 (d)×1 的 NURBS 曲面(d = 曲线阶次)。
|
||||
* 第一排控制点 = 曲线控制点,最后一排 = 偏移后的控制点。
|
||||
*
|
||||
* @param curve 截面曲线
|
||||
* @param direction 拉伸方向(内部归一化)
|
||||
* @param length 拉伸长度
|
||||
* @return 拉伸 NURBS 曲面
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto surface = extrude_curve(profile, {0, 0, 1}, 10.0);
|
||||
* // 沿 Z 轴拉伸 10 单位
|
||||
* @endcode
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsSurface extrude_curve(const NurbsCurve& curve,
|
||||
const Vector3D& direction,
|
||||
double length);
|
||||
|
||||
/// Extract a boundary isoparametric curve from a NURBS surface.
|
||||
/// @param surf Input surface
|
||||
/// @param edge Which boundary: 0=umin, 1=umax, 2=vmin, 3=vmax
|
||||
/// @return NURBS curve along the specified boundary
|
||||
/**
|
||||
* @brief 提取 NURBS 曲面的等参边界曲线
|
||||
*
|
||||
* 沿曲面的 u/v 参数极值提取边界 NURBS 曲线。
|
||||
*
|
||||
* @param surf 输入曲面
|
||||
* @param edge 边界索引:0=u_min, 1=u_max, 2=v_min, 3=v_max
|
||||
* @return 指定边界上的 NURBS 曲线
|
||||
*
|
||||
* @note 边界曲线继承原曲面该方向的阶次和节点向量
|
||||
* @code{.cpp}
|
||||
* auto umin_curve = extract_boundary_curve(surf, 0);
|
||||
* auto vmax_curve = extract_boundary_curve(surf, 3);
|
||||
* @endcode
|
||||
* @ingroup curves
|
||||
*/
|
||||
[[nodiscard]] NurbsCurve extract_boundary_curve(const NurbsSurface& surf, int edge);
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -9,44 +9,154 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/**
|
||||
* @brief NURBS 张量积曲面
|
||||
*
|
||||
* 由 (nu+1)×(nv+1) 控制点网格、权重网格、u/v 节点向量及阶次定义。
|
||||
* 求值公式:
|
||||
* S(u,v) = Σ_i Σ_j N_{i,pu}(u)·N_{j,pv}(v)·w_{ij}·P_{ij}
|
||||
* / Σ_i Σ_j N_{i,pu}(u)·N_{j,pv}(v)·w_{ij}
|
||||
*
|
||||
* 可精确表示球面、圆柱面、锥面等二次曲面。广泛应用于 CAD 数据交换。
|
||||
*
|
||||
* @ingroup curves
|
||||
*/
|
||||
class NurbsSurface {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造 NURBS 曲面
|
||||
* @param control_grid 控制点网格
|
||||
* @param knots_u u 向节点向量
|
||||
* @param knots_v v 向节点向量
|
||||
* @param weights 权重网格,维度与 control_grid 一致,所有值 > 0
|
||||
* @param degree_u u 向阶次
|
||||
* @param degree_v v 向阶次
|
||||
* @code{.cpp}
|
||||
* NurbsSurface sphere(grid, u_knots, v_knots, weights, 3, 3);
|
||||
* @endcode
|
||||
*/
|
||||
NurbsSurface(std::vector<std::vector<Point3D>> control_grid,
|
||||
std::vector<double> knots_u, std::vector<double> knots_v,
|
||||
std::vector<std::vector<double>> weights,
|
||||
int degree_u, int degree_v);
|
||||
|
||||
/**
|
||||
* @brief 求曲面点 S(u,v)
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return 曲面上对应 (u,v) 的 3D 点
|
||||
*/
|
||||
[[nodiscard]] Point3D evaluate(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 向偏导数
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return ∂S/∂u
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_u(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief v 向偏导数
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return ∂S/∂v
|
||||
*/
|
||||
[[nodiscard]] Vector3D derivative_v(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief 单位法向量
|
||||
* @param u u 参数
|
||||
* @param v v 参数
|
||||
* @return N(u,v) = normalize(∂S/∂u × ∂S/∂v)
|
||||
*/
|
||||
[[nodiscard]] Vector3D normal(double u, double v) const;
|
||||
|
||||
/**
|
||||
* @brief u 向阶次
|
||||
* @return pu
|
||||
*/
|
||||
[[nodiscard]] int degree_u() const { return degree_u_; }
|
||||
|
||||
/**
|
||||
* @brief v 向阶次
|
||||
* @return pv
|
||||
*/
|
||||
[[nodiscard]] int degree_v() const { return degree_v_; }
|
||||
|
||||
/**
|
||||
* @brief 控制点数量
|
||||
* @return {nu+1, nv+1},即网格的行数和列数
|
||||
*/
|
||||
[[nodiscard]] std::array<int, 2> num_control_points() const {
|
||||
return {static_cast<int>(cp_.size()), static_cast<int>(cp_.empty()?0:cp_[0].size())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief u 向节点向量
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots_u() const { return knots_u_; }
|
||||
|
||||
/**
|
||||
* @brief v 向节点向量
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<double>& knots_v() const { return knots_v_; }
|
||||
|
||||
/**
|
||||
* @brief 控制点网格访问
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::vector<Point3D>>& control_points() const { return cp_; }
|
||||
|
||||
/**
|
||||
* @brief 权重网格访问
|
||||
* @return 只读引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::vector<double>>& weights() const { return weights_; }
|
||||
|
||||
/// Create a ruled surface between two NURBS curves
|
||||
/**
|
||||
* @brief 由两条 NURBS 曲线创建直纹面
|
||||
* @param c1 起始曲线(v=0 边界)
|
||||
* @param c2 终止曲线(v=1 边界)
|
||||
* @return 直纹 NURBS 曲面
|
||||
* @note 两条曲线需具有兼容的阶次和节点结构(内部做升阶和节点精化对齐)
|
||||
* @see blend_surfaces ruled_surface
|
||||
*/
|
||||
static NurbsSurface ruled(const NurbsCurve& c1, const NurbsCurve& c2);
|
||||
|
||||
/// Create a surface of revolution
|
||||
/**
|
||||
* @brief 由截面曲线旋转生成旋转曲面
|
||||
* @param profile 截面 NURBS 曲线
|
||||
* @param axis_origin 旋转轴上一点
|
||||
* @param axis_dir 旋转轴方向
|
||||
* @param angle_rad 旋转角度(弧度),≤ 2π
|
||||
* @return 旋转 NURBS 曲面
|
||||
* @note 使用 NURBS 表示圆弧的权重方案;完整旋转生成闭曲面
|
||||
* @code{.cpp}
|
||||
* auto rev = NurbsSurface::revolve(profile, origin, {0,0,1}, M_PI);
|
||||
* @endcode
|
||||
*/
|
||||
static NurbsSurface revolve(const NurbsCurve& profile, const Point3D& axis_origin,
|
||||
const Vector3D& axis_dir, double angle_rad);
|
||||
|
||||
/// Tessellate to triangle mesh
|
||||
/**
|
||||
* @brief 将 NURBS 曲面三角化
|
||||
* @param res_u u 方向采样分辨率
|
||||
* @param res_v v 方向采样分辨率
|
||||
* @return {顶点数组, 三角形索引数组},每个三角形为 {i0,i1,i2}
|
||||
* @note 生成 res_u×res_v 均匀网格,每个单元格拆为两个三角形
|
||||
* @see tessellate
|
||||
*/
|
||||
[[nodiscard]] std::pair<std::vector<Point3D>, std::vector<std::array<int,3>>>
|
||||
tessellate(int res_u, int res_v) const;
|
||||
|
||||
private:
|
||||
std::vector<std::vector<Point3D>> cp_;
|
||||
std::vector<double> knots_u_, knots_v_;
|
||||
std::vector<std::vector<double>> weights_;
|
||||
int degree_u_, degree_v_;
|
||||
std::vector<std::vector<Point3D>> cp_; ///< 控制点网格
|
||||
std::vector<double> knots_u_, knots_v_; ///< u/v 向节点向量
|
||||
std::vector<std::vector<double>> weights_; ///< 权重网格
|
||||
int degree_u_, degree_v_; ///< u/v 向阶次
|
||||
};
|
||||
|
||||
} // namespace vde::curves
|
||||
|
||||
@@ -8,23 +8,58 @@ namespace vde::curves {
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
/// Tessellate a parametric surface into a uniform triangle mesh
|
||||
/// @param eval Function(u,v) -> Point3D
|
||||
/// @param res_u, res_v Number of samples in u/v directions
|
||||
/// @return Pairs of (vertices, triangle indices)
|
||||
/**
|
||||
* @brief 均匀参数化曲面三角化
|
||||
*
|
||||
* 在参数域 [0,1]×[0,1] 上生成 res_u×res_v 均匀采样点,
|
||||
* 调用 eval(u,v) 求值后将每个四边形拆为两个三角形。
|
||||
*
|
||||
* @param eval 曲面求值函数 eval(u,v) -> Point3D,参数 (u,v) ∈ [0,1]×[0,1]
|
||||
* @param res_u u 方向子分段数(≥ 1)
|
||||
* @param res_v v 方向子分段数(≥ 1)
|
||||
* @return {顶点数组, 三角形索引数组},三角形为 {i0,i1,i2}
|
||||
*
|
||||
* @note 总顶点数 = (res_u+1)×(res_v+1),三角形数 = 2×res_u×res_v
|
||||
* @code{.cpp}
|
||||
* auto [verts, tris] = tessellate(
|
||||
* [&](double u, double v) { return surface.evaluate(u,v); },
|
||||
* 20, 20);
|
||||
* @endcode
|
||||
* @see adaptive_tessellate
|
||||
* @ingroup curves
|
||||
*/
|
||||
std::pair<std::vector<Point3D>, std::vector<std::array<int, 3>>>
|
||||
tessellate(const std::function<Point3D(double,double)>& eval,
|
||||
int res_u, int res_v);
|
||||
|
||||
/// Adaptive tessellation based on normal-angle deviation
|
||||
/// Recursively subdivides the parameter domain when the angular deviation
|
||||
/// between corner normals exceeds the threshold.
|
||||
/// @param eval Function(u,v) -> Point3D
|
||||
/// @param normal_fn Function(u,v) -> Vector3D (unit normal)
|
||||
/// @param min_res Minimum subdivisions per direction (1 = single quad)
|
||||
/// @param max_depth Maximum recursion depth (limits total subdivisions)
|
||||
/// @param angle_threshold_deg Maximum normal-angle deviation in degrees
|
||||
/// @return Pairs of (vertices, triangle indices)
|
||||
/**
|
||||
* @brief 自适应曲面三角化
|
||||
*
|
||||
* 基于法向角度偏差的自适应细分策略:
|
||||
* 从 min_res×min_res 初始网格开始,对每个四边形检查四个角点的法向偏差。
|
||||
* 若任意相邻角点间的法向夹角超过 angle_threshold_deg,则将该四边形四分细分。
|
||||
*
|
||||
* 相比均匀 tessellate() 的优点:
|
||||
* - 曲率大的区域自动增加采样密度
|
||||
* - 平坦区域保持粗糙网格,节省面数
|
||||
*
|
||||
* @param eval 曲面求值函数 eval(u,v) -> Point3D
|
||||
* @param normal_fn 单位法向函数 normal_fn(u,v) -> Vector3D
|
||||
* @param min_res 初始每向子分段数(≥ 1),默认 2
|
||||
* @param max_depth 最大递归深度,限制总面数,默认 6
|
||||
* @param angle_threshold_deg 法向角度偏差阈值(度),默认 5.0
|
||||
* @return {顶点数组, 三角形索引数组}
|
||||
*
|
||||
* @note 最终面数上限约为 2·min_res²·4^max_depth(实际远小于此上限)
|
||||
* @code{.cpp}
|
||||
* auto [verts, tris] = adaptive_tessellate(
|
||||
* [&](double u, double v) { return surf.evaluate(u,v); },
|
||||
* [&](double u, double v) { return surf.normal(u,v); },
|
||||
* 2, 5, 3.0); // 3° 偏差阈值,最大深度 5
|
||||
* @endcode
|
||||
* @see tessellate
|
||||
* @ingroup curves
|
||||
*/
|
||||
std::pair<std::vector<Point3D>, std::vector<std::array<int, 3>>>
|
||||
adaptive_tessellate(
|
||||
const std::function<Point3D(double,double)>& eval,
|
||||
|
||||
Reference in New Issue
Block a user