#pragma once #include "vde/core/point.h" #include 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 control_points, std::vector 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 domain() const; /** * @brief 控制点访问 * @return 控制点数组只读引用 */ [[nodiscard]] const std::vector& control_points() const { return cp_; } /** * @brief 节点向量访问 * @return 节点向量只读引用 */ [[nodiscard]] const std::vector& 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 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 basis_derivatives(double t, int span = -1) const; private: std::vector cp_; ///< 控制点序列 std::vector knots_; ///< 节点向量(非递减) int degree_; ///< 曲线阶次 }; } // namespace vde::curves