You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构多阶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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:07:59