123 lines
4.0 KiB
C++
123 lines
4.0 KiB
C++
#pragma once
|
||
#include "vde/core/point.h"
|
||
#include <vector>
|
||
|
||
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_; }
|
||
|
||
/**
|
||
* @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;
|
||
|
||
/**
|
||
* @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;
|
||
|
||
/**
|
||
* @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_; ///< 曲线阶次
|
||
};
|
||
|
||
} // namespace vde::curves
|