如何重构多阶Bezier曲线类?实现统一接口与动态对象创建
需求完全可行,以下是两种实现方案
方案一:抽象基类 + 模板派生类(推荐,符合面向对象多态)
先定义统一的抽象基类,确保所有Bezier类遵循相同接口,再用模板类处理不同控制点数量的实现:
#pragma once #include "glm/vec2.hpp" #include <memory> #include <array> struct Custom { float distance; glm::vec2 point; }; // 抽象基类,定义统一接口 class AbstractBezier { public: virtual ~AbstractBezier() = default; virtual glm::vec2 operator()(float t) const = 0; virtual glm::vec2 derivate(float t) const = 0; // 统一接口:将Bezier3的额外参数设为可选指针 virtual Custom findClosestPoint(const glm::vec2& point, double* start = nullptr) const = 0; virtual size_t getCount() const = 0; }; // 模板类处理2-4个控制点的Bezier曲线 template<size_t ControlPointCount> class Bezier : public AbstractBezier { static_assert(ControlPointCount >= 2 && ControlPointCount <=4, "仅支持2-4个控制点"); public: std::array<glm::vec2, ControlPointCount> m_controlPoints; size_t m_count = 100; // 可变参数构造函数,接收控制点序列和采样数 template<typename... Points> Bezier(Points&&... points, size_t count) : m_controlPoints{std::forward<Points>(points)...}, m_count(count) {} glm::vec2 operator()(float t) const override { // 编译期分支处理不同阶数的贝塞尔计算 if constexpr (ControlPointCount == 2) { return (1-t)*m_controlPoints[0] + t*m_controlPoints[1]; } else if constexpr (ControlPointCount ==3) { float t1 = 1 - t; return t1*t1*m_controlPoints[0] + 2*t1*t*m_controlPoints[1] + t*t*m_controlPoints[2]; } else if constexpr (ControlPointCount ==4) { float t1 = 1 - t; return t1*t1*t1*m_controlPoints[0] + 3*t1*t1*t*m_controlPoints[1] + 3*t1*t*t*m_controlPoints[2] + t*t*t*m_controlPoints[3]; } } glm::vec2 derivate(float t) const override { if constexpr (ControlPointCount ==2) { return m_controlPoints[1] - m_controlPoints[0]; } else if constexpr (ControlPointCount ==3) { float t1 = 1 - t; return 2*t1*(m_controlPoints[1]-m_controlPoints[0]) + 2*t*(m_controlPoints[2]-m_controlPoints[1]); } else if constexpr (ControlPointCount ==4) { float t1 = 1 - t; return 3*t1*t1*(m_controlPoints[1]-m_controlPoints[0]) + 6*t1*t*(m_controlPoints[2]-m_controlPoints[1]) + 3*t*t*(m_controlPoints[3]-m_controlPoints[2]); } } Custom findClosestPoint(const glm::vec2& point, double* start) const override { Custom result; // 替换为你原有Bezier1/2/3的findClosestPoint实现 if constexpr (ControlPointCount ==4) { // 处理Bezier3的start参数逻辑 } else { // Bezier1/2的逻辑,忽略start参数 } return result; } size_t getCount() const override { return m_count; } }; // 工厂函数:根据控制点数量创建对应Bezier对象 std::unique_ptr<AbstractBezier> createBezier(std::initializer_list<glm::vec2> points, size_t count =100) { switch(points.size()) { case 2: return std::make_unique<Bezier<2>>(*points.begin(), *(points.begin()+1), count); case3: return std::make_unique<Bezier<3>>(*points.begin(), *(points.begin()+1), *(points.begin()+2), count); case4: return std::make_unique<Bezier<4>>(*points.begin(), *(points.begin()+1), *(points.begin()+2), *(points.begin()+3), count); default: throw std::invalid_argument("控制点数量必须为2-4个"); } }
使用示例:
// 统一接口的处理函数 void processCurve(const AbstractBezier& curve) { glm::vec2 midPoint = curve(0.5f); glm::vec2 midDeriv = curve.derivate(0.5f); // ...其他逻辑 } int main() { auto linear = createBezier({glm::vec2(0,0), glm::vec2(1,1)}, 200); auto quadratic = createBezier({glm::vec2(0,0), glm::vec2(0.5,1), glm::vec2(1,0)}, 200); auto cubic = createBezier({glm::vec2(0,0), glm::vec2(0.25,1), glm::vec2(0.75,-1), glm::vec2(1,0)}, 200); processCurve(*linear); processCurve(*quadratic); processCurve(*cubic); return 0; }
核心优势:
- 编译期通过
constexpr分支处理不同阶数,无运行时性能损耗 - 多态接口清晰,符合面向对象设计规范
- 工厂函数简化对象创建逻辑
方案二:使用std::variant封装原有类(无继承,快速改造)
如果不想修改原有类的结构,可以用std::variant封装不同阶数的Bezier类,通过访问器统一调用方法:
#pragma once #include "glm/vec2.hpp" #include <variant> struct Custom { float distance; glm::vec2 point; }; // 保留原有Bezier类,统一findClosestPoint接口(添加可选参数) class Bezier1 { public: glm::vec2 m_P_0, m_P_1; size_t m_Count = 100; Bezier1(const glm::vec2& p0, const glm::vec2& p1, size_t count) : m_P_0(p0), m_P_1(p1), m_Count(count) {} glm::vec2 operator()(float t) const { return (1-t)*m_P_0 + t*m_P_1; } glm::vec2 derivate(float t) const { return m_P_1 - m_P_0; } Custom findClosestPoint(const glm::vec2& point, double* start = nullptr) const { // 原有Bezier1的实现 return {}; } }; class Bezier2 { public: glm::vec2 m_P_0, m_P_1, m_P_2; size_t m_Count = 100; Bezier2(const glm::vec2& p0, const glm::vec2& p1, const glm::vec2& p2, size_t count) : m_P_0(p0), m_P_1(p1), m_P_2(p2), m_Count(count) {} glm::vec2 operator()(float t) const { float t1 = 1-t; return t1*t1*m_P_0 + 2*t1*t*m_P_1 + t*t*m_P_2; } glm::vec2 derivate(float t) const { float t1 =1-t; return 2*t1*(m_P_1 - m_P_0) + 2*t*(m_P_2 - m_P_1); } Custom findClosestPoint(const glm::vec2& point, double* start = nullptr) const { // 原有Bezier2的实现 return {}; } }; class Bezier3 { public: glm::vec2 m_P_0, m_P_1, m_P_2, m_P_3; size_t m_Count = 100; Bezier3(const glm::vec2& p0, const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3, size_t count) : m_P_0(p0), m_P_1(p1), m_P_2(p2), m_P_3(p3), m_Count(count) {} glm::vec2 operator()(float t) const { float t1 =1-t; return t1*t1*t1*m_P_0 +3*t1*t1*t*m_P_1 +3*t1*t*t*m_P_2 +t*t*t*m_P_3; } glm::vec2 derivate(float t) const { float t1 =1-t; return 3*t1*t1*(m_P_1 - m_P_0) +6*t1*t*(m_P_2 - m_P_1) +3*t*t*(m_P_3 - m_P_2); } Custom findClosestPoint(const glm::vec2& point, double* start = nullptr) const { // 原有Bezier3的实现,使用start参数 return {}; } }; // 定义变体类型,包含所有Bezier类 using BezierVariant = std::variant<Bezier1, Bezier2, Bezier3>; // 辅助函数:统一访问变体中的Bezier对象 template<typename Func> auto visitBezier(const BezierVariant& bezier, Func&& func) { return std::visit(std::forward<Func>(func), bezier); }
使用示例:
// 统一接口的处理函数 void processCurve(const BezierVariant& curve) { auto midPoint = visitBezier(curve, [](const auto& b) { return b(0.5f); }); auto midDeriv = visitBezier(curve, [](const auto& b) { return b.derivate(0.5f); }); // ...其他逻辑 } int main() { BezierVariant linear = Bezier1(glm::vec2(0,0), glm::vec2(1,1), 200); BezierVariant quadratic = Bezier2(glm::vec2(0,0), glm::vec2(0.5,1), glm::vec2(1,0), 200); BezierVariant cubic = Bezier3(glm::vec2(0,0), glm::vec2(0.25,1), glm::vec2(0.75,-1), glm::vec2(1,0), 200); processCurve(linear); processCurve(quadratic); processCurve(cubic); return 0; }
核心优势:
- 无需修改原有类的继承关系,适合快速迭代
std::variant编译期安全,确保所有类型都被处理
内容的提问来源于stack exchange,提问作者julien
相关产品推荐
相关产品推荐

