精华内容
下载资源
问答
  • 本项目为vs2013工程项目,贝塞尔曲线计算,控制点可以多个,支持二维数据,三维数据,使用c++语言编写,直接打开即可运行。
  • C++实现的三次贝塞尔曲线绘制,可拖动控制点修改曲线形状,包含源代码和可执行文件,VS2008的工程。 C++实现的三次贝塞尔曲线绘制,可拖动控制点修改曲线形状,包含源代码和可执行文件,VS2008的工程。
  • 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们...
  • 贝塞尔曲线c++实现。鼠标右键创建端点,左键移动端点,中键回溯。可以用vs或QT creator打开。这是完整的项目文件夹,保证编译通过。新版更新了优化了一下代码,解决了右键拖动经常拖不动的问题。
  • 由于用计算机画图大部分时间是操作鼠标来掌握线条的路径,与手绘的感觉和效果有很大的差别。即使是一位精明的画师能...本文则记录如何使用贝塞尔曲线定制平滑的动画效果,并使用C++编写了cmd动画和窗口动画示例代码。

    贝塞尔曲线简介

    由于用计算机画图大部分时间是操作鼠标来掌握线条的路径,与手绘的感觉和效果有很大的差别。即使是一位精明的画师能轻松绘出各种图形,拿到鼠标想随心所欲的画图也不是一件容易的事。这一点是计算机万万不能代替手工的工作,所以人们只能颇感无奈。使用贝塞尔工具画图很大程度上弥补了这一缺憾。贝塞尔曲线是计算机图形图像造型的基本工具,是图形造型运用得最多的基本线条之一。它通过控制曲线上的四个点(起始点、终止点以及两个相互分离的中间点)来创造、编辑图形。
    除此之外,贝塞尔曲线还经常用来做动画,让动画过渡更平滑。本文则记录如何使用贝塞尔曲线定制平滑的动画效果,并使用C++编写了cmd动画和窗口动画示例代码。

    一阶贝塞尔

    一阶贝塞尔曲线
    设定图中运动的点为Ptt为运动时间,t∈(0,1),可得如下公式:
    公式1

    二阶贝塞尔

    二阶贝塞尔曲线
    在二阶贝塞尔曲线中,已知三点恒定(P0,P1,P2),设定在P0 P1中的点为Pa,在P1 P2中的点为PbPtPa Pb上的点,这三点都在相同时间t内做匀速运动。

    由公式(1)可知
    公式2,3,4
    将公式(2)(3)代入公式(4)中,可得
    公式5

    三阶贝塞尔

    三阶贝塞尔曲线
    同理,根据以上的推导过程可得
    公式6
    由此可以推导
    公式7

    N阶贝塞尔曲线

    四阶贝塞尔曲线:
    四阶贝塞尔曲线
    五阶贝塞尔曲线:
    五阶贝塞尔曲线
    N阶贝塞尔曲线公式:
    N阶贝塞尔曲线公式

    贝塞尔曲线在动画中的应用

    • 贝赛尔曲线广泛应用于绘图软件中,例如Adobe PhotoShop、Adobe Flash。
    • Android可以通过自定义的view来实现贝塞尔曲线
    • ios则可以使用UIBezierPath类来生成贝塞尔曲线
    • 前端,canvas bezierCurveTo,css animation-timing-function: cubic-bezier(x,x,x,x}都有关于贝赛尔曲线的一些应用。

    贝塞尔曲线在动画中的应用一般是三阶贝塞尔曲线,通过两个控制点来描述一般的动画曲线。通常以动画完成度为y轴,时间为x轴,然后将时间带入动画曲线求得对应的动画完成度

    但是上述公式描述的是点与点关系,想要分解为x,y坐标的关系,则需要继续推导,以三阶为例:贝塞尔曲线x与y坐标的关系
    想要直观的感受曲线的效果可以前往: cubic-bezier
    得到xy坐标关系后即可写代码进行实践了。

    实践

    求曲线散点坐标

    由上面推导的曲线点的坐标和时间的关系可得:设曲线起点为(0,0),终点为(1,1),则t时刻点的位置仅与两个控制点P1 P2有关。先定义表示一个点的结构体:

      struct PointF
      {
        PointF() : x(0), y(0) {}
        PointF(double x, double y) : x(x), y(y) {}
        double x;
        double y;
      };
    

    然后传入两个点的坐标进行初始化

      void init(double x1, double y1, double x2, double y2)
      {
        pc.x = 3.0 * x1;
        pb.x = 3.0 * (x2 - x1) - pc.x;
        pa.x = 1.0 - pc.x - pb.x;
    
        pc.y = 3.0 * y1;
        pb.y = 3.0 * (y2 - y1) - pc.y;
        pa.y = 1.0 - pc.y - pb.y;
      }
    

    计算t时刻的xy坐标

      double calcX(double t)
      {
        return ((pa.x * t + pb.x) * t + pc.x) * t;
      }
    
      double calcY(double t)
      {
        return ((pa.y * t + pb.y) * t + pc.y) * t;
      }
    

    根据给定的采样计算出[0,1]时刻的n个曲线上的点坐标,即可绘制出曲线图。

      for (int i = 0; i < size; ++i) { // size即为采样个数,然后计算出对应采样时刻的曲线坐标
        sample_[i] = PointF(calcX(i * 1.0 / size), calcY(i * 1.0 / size));
      }
    

    得到曲线坐标后可绘制曲线图看下:
    贝塞尔曲线图
    其中蓝色的曲线是贝塞尔曲线,绿色和红色的曲线分别表示贝塞尔曲线上的y坐标和x坐标与t的关系,即y随时间先慢,再快,最后慢。x随时间先快,再变慢,最后变快。

    将曲线应用到动画

    得到曲线散点坐标后,该怎么将其应用到动画呢?
    因为我们已经设了x坐标在[0,1]之间,而动画一般就是分为动画完成度和时间的关系,而我们设动画的时间也在[0,1],那么就可以给定动画的时刻t,然后通过曲线散点坐标求得对应的动画完成度。
    即通过x坐标求y坐标,因为我们只有散点坐标,时刻t不一定跟已有点的x坐标相同,因此需要找到最接近的时刻t的两个点进行插值,即可求得近似的y坐标,也即动画完成度。
    废话不多说,直接上代码,使用二分法查找最近的两点并插值求y:

    double GetYAtX(double x)
    {
      int head = 0;
      int tail = size - 1;
      int center;
      while (head <= tail) {
        center = (head + tail) / 2;
        if (sample_[center].x < x) {
          head = center + 1;
        } else if (sample_[center].x > x) {
          tail = center - 1;
        } else {
          break;
        }
      }
    
      if (head < size - 1) {
        double x0 = sample_[head].x;
        double x1 = sample_[head + 1].x;
        double y0 = sample_[head].y;
        double y1 = sample_[head + 1].y;
        return (x - x0) / (x1 - x0) * (y1 - y0) + y0; // 线性插值计算
      } else {
        return 1;
      }
    }
    
    动画框架

    现在时刻t的动画完成度能求了,接下来就是实验一下动画效果了,顺便封装一个简单的动画框架,这样就能方便的进行各种动画效果。

    1. 首先封装一下贝塞尔曲线的求值,BezierCurve
    2. 再封装做动画的类,Animator

    封装完后即可开始使用

    cmd动画

    先来个cmd的动画试试,其中封装了个Console
    示例代码:

    int main()
    {
      std::getc(stdin);
    
      std::thread thread(
        []() 
        {
          
          draw::Rectangle r(10, 2);
          int fps = 70;
          int64_t interval = 1000 / fps;
          Animator animator(0, 150, 1, 
                            [&](int v) 
                            { 
                              r.SetPos(40 + v, 10);
                              r.Draw();
                            }
          , EasingCurve::Type::InOutBezier);
          auto last_time = std::chrono::steady_clock::now();
          bool forward = true;
          while (true) {
            while (!animator.Step(1.0 / fps, forward)) {
              auto now = std::chrono::steady_clock::now();
              auto delay = (interval - std::chrono::duration_cast<std::chrono::milliseconds>((now - last_time)).count());
              std::this_thread::sleep_for(std::chrono::milliseconds(delay > 0 ? delay : 1)); // 最低1ms免得卡
              last_time = std::chrono::steady_clock::now();
            }
            forward = !forward;
          }
          
        }
      );
    
      thread.join();
      std::cout << std::endl;
    }
    

    分别实验了根据两种曲线进行动画的效果,通过cmd动画和网站上动画的对比,可以看出还是很接近的。

    • InOutBezier: 参数(.63, 0, .37, 1)
      曲线图:
      InOutBezier
      效果图:
      InOutBezier

    • InOutBounceBezier:参数(.56, -0.48, .46, 1.5)
      贝塞尔曲线:
      在这里插入图片描述

      效果图:
      InOutBounceBezier

    窗口动画
    • InOutBezier:参数(.63, 0, .37, 1)
      曲线图:
      InOutBezier
      效果图:
      gui-inoutbezier

    • InOutBounceBezier:参数(.56, -0.48, .46, 1.5)
      贝塞尔曲线:
      在这里插入图片描述
      效果图:
      在这里插入图片描述

    完整代码

    示例代码

    贝塞尔动画(csdn下载)
    贝塞尔动画(github)

    核心类代码
    BezierCurve
    #pragma once
    
    struct BezierEase
    {
      BezierEase(double x1, double y1, double x2, double y2)
      {
        init(x1, y1, x2, y2);
        for (int i = 0; i < size; ++i) {
          sample_[i] = PointF(calcX(i * 1.0 / size), calcY(i * 1.0 / size));
        }
      }
    
      double value(double t)
      {
        return GetYAtX(t);
      }
    
      double GetYAtX(double x)
      {
        int head = 0;
        int tail = size - 1;
        int center;
        while (head <= tail) {
          center = (head + tail) / 2;
          if (sample_[center].x < x) {
            head = center + 1;
          } else if (sample_[center].x > x) {
            tail = center - 1;
          } else {
            break;
          }
        }
    
        if (head < size - 1) {
          double x0 = sample_[head].x;
          double x1 = sample_[head + 1].x;
          double y0 = sample_[head].y;
          double y1 = sample_[head + 1].y;
          return (x - x0) / (x1 - x0) * (y1 - y0) + y0; // 线性插值计算
        } else {
          return 1;
        }
      }
    private:
      struct PointF
      {
        PointF() : x(0), y(0) {}
        PointF(double x, double y) : x(x), y(y) {}
        double x;
        double y;
      };
    
      double calcX(double t)
      {
        return ((pa.x * t + pb.x) * t + pc.x) * t;
      }
    
      double calcY(double t)
      {
        return ((pa.y * t + pb.y) * t + pc.y) * t;
      }
    
      void init(double x1, double y1, double x2, double y2)
      {
        pc.x = 3.0 * x1;
        pb.x = 3.0 * (x2 - x1) - pc.x;
        pa.x = 1.0 - pc.x - pb.x;
    
        pc.y = 3.0 * y1;
        pb.y = 3.0 * (y2 - y1) - pc.y;
        pa.y = 1.0 - pc.y - pb.y;
      }
      PointF pa;
      PointF pb;
      PointF pc;
      const static int size = 1001;
      PointF sample_[size];
    };
    
    Animator
    #pragma once
    
    #include "bezier_curve.h"
    
    class EasingCurve
    {
    public:
      enum Type
      {
        InOutBezier, InOutBounceBezier
      };
    
      EasingCurve(Type t)
      {
        switch (t) {
          case InOutBounceBezier:
            curve_.reset(new BezierEase(.56, -0.48, .46, 1.5));
            break;
          case InOutBezier:
            curve_.reset(new BezierEase(.63, 0, .37, 1));
          default:
            break;
        }
      }
    
      // [0 - 1]
      double valueForProgress(double t)
      {
        t = max(0, min(t, 1));
        return curve_->value(t);
      }
    private:
      std::unique_ptr<BezierEase> curve_;
    };
    
    template<typename T = int>
    class Animator_
    {
    public:
      Animator_(T from, T to, double duration/*单位秒*/, std::function<void(T)> fn = nullptr, EasingCurve::Type type = EasingCurve::InOutBezier)
        : ec_(type), from_(from), to_(to), duration_(duration), fn_(fn)
      {
      }
      Animator_() : ec_(EasingCurve::InOutBezier) {}
      bool Step(double interval/*单位秒*/, bool forward = true)
      {
        assert(interval > 0);
        double time_now = current_time_ + (forward ? interval : (-interval));
        bool isFinished = forward ? time_now >= duration_ : time_now <= 0;
        time_now = forward ? min(time_now, duration_) : max(time_now, 0);
        T val = duration_ == 0 ? (forward ? to_ : from_) : from_ + T((to_ - from_) *ec_.valueForProgress(time_now / duration_));
        current_time_ = time_now;
        if (fn_) fn_(val);
        return isFinished;
      }
    public:
      void SetRange(T from_val, T to_value) { from_ = from_val; to_ = to_value; }
      void SetDuration(double val) { duration_ = val; }
      void SetFn(std::function<void(T)> val) { fn_ = val; }
      void ResetCurrentTime() { current_time_ = 0; }
      void SetCurrentTime(double current_time) { current_time_ = current_time; }
      void Complete(bool state_forward) { SetCurrentTime(state_forward ? duration_ : 0); }
    private:
      T from_ = {};
      T to_ = {};
      double duration_ = 0;
      double current_time_ = 0;	//当前时间
      std::function<void(T)> fn_ = nullptr;
      EasingCurve ec_;
    };
    
    using Animator = Animator_<>;
    
    Console
    #pragma once
    #include <iostream>
    
    class Console
    {
    private:
      struct Cursor
      {
      public:
    
        // up down是y变化,x不变
        void up(int n = 1)
        {
          std::cout << "\033[" << n << "A";
        }
    
        void down(int n = 1)
        {
          std::cout << "\033[" << n << "B";
        }
    
        void right(int n = 1)
        {
          std::cout << "\033[" << n << "C";
        }
    
        void left(int n = 1)
        {
          std::cout << "\033[" << n << "D";
        }
    
        void save()
        { // 保存当前位置
          std::cout << "\0337";
        }
    
        void restore()
        { // 回到保存的位置
          std::cout << "\0338";
        }
    
        void nextLine(int n = 1)
        { // 光标以当前位置为起始位置,往下到第n行开头
          std::cout << "\033[" << n << "E";
        }
    
        void previousLine(int n = 1)
        { // 光标以当前位置为起始位置,网上到第n行开头
          std::cout << "\033[" << n << "F";
        }
    
        void move(int y = 0, int x = 0)
        { // 以当前屏幕为原点(0,0)移动光标
          std::cout << "\033[" << y << ";" << x << "H";
        }
    
        void enableBlinking(bool enable = false)
        {
          std::cout << "\033[?12" << (enable ? "h" : "l");
        }
    
        void hideCursor(bool hide = true)
        {
          std::cout << "\033[?25" << (hide ? "l" : "h");
        }
      };
    public:
      Console()
      {
        const auto h_out = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD mode;
    
        GetConsoleMode(h_out, &mode);
        SetConsoleMode(h_out, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
      }
      enum Color
      {
        Black = 0,
        Red,
        Green,
        Yellow,
        Blue,
        Magenta,
        Cyan,
        White
      };
    
      void underline()
      {
        setTextAttr(4);
      }
    
      void noUnderline()
      {
        setTextAttr(24);
      }
    
      void bright()
      { /* 设置前景颜色变亮 */
        setTextAttr(1);
      }
    
      void normal()
      {
        setTextAttr(0);
      }
    
      void negative()
      { /* 前景色和背景色交换 */
        setTextAttr(7);
      }
    
      void positive()
      { /* 将前景色和背景色恢复正常 */
        setTextAttr(27);
      }
    
      void setForeColor(Color color, bool bright = false)
      {
        setTextAttr(bright ? 90 + color : 30 + color);
      }
    
      void setBackColor(Color color, bool bright = false)
      {
        setTextAttr(bright ? 100 + color : 40 + color);
      }
    
      void setScrollRegion(int top, int bottom)
      {
        std::cout << "\033[" << top << ";" << bottom << "r";
      }
    
      void clearCurLine()
      {
        std::cout << "\033[K";
      }
    
      void setTitle(const std::string &title)
      {
        std::cout << "\033]2;" << title << "\x07";
      }
    
      int width()
      {
        auto info = getScreenBufferInfo();
        return info.srWindow.Right - info.srWindow.Left + 1;
      }
    
      int height()
      {
        auto info = getScreenBufferInfo();
        return info.srWindow.Bottom - info.srWindow.Top + 1;
      }
    public:
      Cursor cursor;
    
    private:
      void setTextAttr(int n)
      {
        std::cout << "\033[" << n << "m";
      }
    
      CONSOLE_SCREEN_BUFFER_INFO getScreenBufferInfo()
      {
        CONSOLE_SCREEN_BUFFER_INFO info;
        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
        return info;
      }
    };
    

    参考资料

    1. 从零开始学图形学:10分钟看懂贝塞尔曲线
    2. 如何理解并应用贝塞尔曲线
    3. JS模拟CSS3动画-贝塞尔曲线
    4. 求高手解答 贝塞尔曲线问题
    5. cubic-bezier:贝塞尔曲线在线预览网站
    展开全文
  • 三次贝塞尔曲线 鼠标控制
  • C++实现二阶 三阶贝塞尔曲线

    千次阅读 2020-03-05 16:21:16
    C++实现贝塞尔曲线 已经存在许多讲解得比较清楚的解释。如https://www.jianshu.com/p/0c9b4b681724 贝塞尔曲线包含多阶。贝塞尔曲线的阶数和次数是一样的,二阶贝塞尔,三个点,最高次数二次。例:二阶贝塞尔:三个...

    C++实现贝塞尔曲线

    已经存在许多讲解得比较清楚的解释。如https://www.jianshu.com/p/0c9b4b681724

    贝塞尔曲线包含多阶。贝塞尔曲线的阶数和次数是一样的,二阶贝塞尔,三个点,最高次数二次。例:二阶贝塞尔:三个点,两个线段,以所有等比的点组合成的曲线叫做二阶贝塞尔曲线。如下图。
    在这里插入图片描述

    在这里插入图片描述

    根据贝塞尔曲线的公式直接根据控制点缺点阶数,。
    在这里插入图片描述
    定义.h头文件

    class Bezier {
    public:
      Bezier() = default;
      const std::vector<hybrid_astar::Vector2D>& CalculateSpline(
          const std::vector<hybrid_astar::Vector2D>& control_points,
          const int& points_number);
      void CalculateSecond();
      void CalculateCubic();
    private:
      int ctrl_num_;  //控制点数量
      int order_;     //阶数,表示曲线为几阶曲线,为控制点数-1
      int knot_num_;  //节点数是分布在0-1之间有多少个点
      std::vector<hybrid_astar::Vector2D> control_points_;
      std::vector<hybrid_astar::Vector2D> path_points_;
    
    };
    

    定义cpp文件

    const std::vector<hybrid_astar::Vector2D>& Bezier::CalculateSpline(
        const std::vector<hybrid_astar::Vector2D>& control_points,
        const int& knot_number) {
      ctrl_num_ = control_points.size();
      control_points_ = control_points;
      knot_num_ = knot_number;
      //点数-1为阶数l
      order_ = control_points_.size() - 1;
      switch (order_) {
        case 2:
          CalculateSecond();
          break;
        case 3:
          CalculateCubic();
          break;
        default:
          std::cout << "No define order: " << order_ << std::endl;
          break;
      }
      return path_points_;
    }
    
    void Bezier::CalculateSecond() {
      if (ctrl_num_ != 3)
        return;
      path_points_.clear();
      double t = 0;
      double delta_t = 1.0 / (static_cast<double>(knot_num_));
      double f1, f2, f3;
      for (double i = 0; i <= knot_num_; ++i) {
        t = delta_t * i;
        f1 = (1 - t) * (1 - t);
        f2 = 2 * (1 - t) * t;
        f3 = t * t;
        path_points_.push_back(
              hybrid_astar::Vector2D(f1 * control_points_[0].getX() +
              f2 * control_points_[1].getX() + f3 * control_points_[2].getX(),
            f1 * control_points_[0].getY() + f2 * control_points_[1].getY() +
            f3 * control_points_[2].getY()));
      }
    }
    
    void Bezier::CalculateCubic() {
      if (ctrl_num_ != 4)
        return;
      path_points_.clear();
      double t = 0;
      double delta_t = 1.0 / (static_cast<double>(knot_num_));
      double f1, f2, f3, f4;
      for (double i = 0; i <= knot_num_; ++i) {
        t = delta_t * i;
        f1 = (1 - t) * (1 - t) * (1 - t);
        f2 = 3 * (1 - t) * (1 - t) * t;
        f3 = 3 * (1 - t) * t * t;
        f4 = t * t * t;
        path_points_.push_back(
              hybrid_astar::Vector2D(f1 * control_points_[0].getX() +
              f2 * control_points_[1].getX() + f3 * control_points_[2].getX() +
            f4 * control_points_[3].getX(),
            f1 * control_points_[0].getY() + f2 * control_points_[1].getY() +
            f3 * control_points_[2].getY() + f4 * control_points_[3].getY()));
      }
    }
    
    展开全文
  • 贝塞尔曲线的原理(BezierCruve) 查看原理戳我 这里就不介绍推导过程了,值得注意的是文章中的线段都是矢量,然后线段都可以被拆分成两个坐标的差来表示AB=B−AAB=B-AAB=B−A一开始看有点蒙蔽,代入这个等式后,...

    贝塞尔曲线的原理(BezierCruve)

    查看原理戳我
    这里就不介绍推导过程了,值得注意的是文章中的线段都是矢量,然后线段都可以被拆分成两个坐标的差来表示 A B = B − A AB=B-A AB=BA一开始看有点蒙蔽,代入这个等式后,之后所有的推导都可以理解。这里就直接介绍一下结论 P i k ( t ) = { P i ( t ) k=0 ( 1 − t ) P i k − 1 ( t ) + t P i + 1 k − 1 ( t ) , k=1,2,3 … n;i=0,1,2 … n-k P_i^k(t)= \begin{cases} P_i(t) &amp; \text {k=0} \\ (1-t)P_i^{k-1}(t)+tP_{i+1}^{k-1}(t), &amp; \text{k=1,2,3…n;i=0,1,2…n-k} \end{cases} Pik(t)={Pi(t)(1t)Pik1(t)+tPi+1k1(t),k=0k=1,2,3n;i=0,1,2n-k原文中的介绍没有带t,一开始以为 P i k P_i^k Pik是常量,觉得十分诡异,后来重新看了下推导过程,其实都是变量,这里补上自变量t,并且这里的 k k k不是指k次方的意思,是指第k阶,可以理解为一个区分开来的不同阶数的上标。

    贝塞尔曲线的理解

    首先从结果的公式上来看

    1. 一条贝塞尔至少要有两个控制点(此时的贝塞尔曲线为一条直线)

    控制点: 当k等于0时,对应的 P i k ( t ) P_i^k(t) Pik(t),即输入点

    1. 这是一条迭代公式,每次迭代都会少掉一个“点”。

    “点”: P i + 1 P_{i+1} Pi+1,可以看到每次的每次新迭代生成的 P i k ( t ) P_i^k(t) Pik(t)都由 ( 1 − t ) P i k − 1 ( t ) + t P i + 1 k − 1 ( t ) (1-t)P_i^{k-1}(t)+tP_{i+1}^{k-1}(t) (1t)Pik1(t)+tPi+1k1(t)生成,所以第k次迭代的时候,只剩下n-k个点,即 i &lt; = n − k i&lt;=n-k i<=nk

    1. 我们最终要得到的贝塞尔曲线是 P 0 n ( t ) P_0^n(t) P0n(t),所以迭代的终止条件便是i得最大值只能为1(因为从0开始计数),即 P 0 n ( t ) = ( 1 − t ) P 0 n − 1 ( t ) + t P 1 n − 1 ( t ) P_0^n(t)=(1-t)P_0^{n-1}(t)+tP_{1}^{n-1}(t) P0n(t)=(1t)P0n1(t)+tP1n1(t)
    2. 贝塞尔曲线必然经过第一个控制点和最后一个控制点。

    贝塞尔曲线的编程实现

    1. 由于贝塞尔曲线本身的数学表达式便是一条递归式,所以决定采用递归的方式来实现。
    2. 由于都是对点进行四则运算,直接采用opencv的数据结构,都已经重载好了,顺便可以用opencv来显示结果。
    3. 当然如果不需要显示和不嫌麻烦的话,可以自己写Point2f这个结构,并且重载乘法运算符,也是可以实现的,那样就不用调用opencv了。
    
    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include <vector>
    using namespace cv;
    using std::cout;
    using std::endl;
    using std::vector;
    vector<Point2f> bezierCurve(vector<Point2f> src);
    int main(int argc, char const* argv[])
    {
       while (1) {
           vector<Point2f> path;
           CvRNG rng;
           rng = cvRNG(cvGetTickCount());
           for (int i = 1; i < 6; i++)
               path.push_back(Point2f(i * 800 / 6.0, cvRandInt(&rng) % 800));
    
           Mat img(800, 800, CV_8UC3);
           img = 0;
           for (int i = 0; i < path.size(); i++)
               circle(img, path[i], 3, Scalar(0, 0, 255), 3); //BGR
           vector<Point2f> bezierPath = bezierCurve(path);
           for (int i = 0; i < bezierPath.size(); i++) {
               //circle(img, bezierPath[i], 3, Scalar(0, 255, 255), 3); //BGR
               img.at<cv::Vec3b>(cvRound(bezierPath[i].y), cvRound(bezierPath[i].x)) = { 0, 255, 255 };
           }
           imshow("black", img);
           if (waitKey(0) == 'q')
               break;
       }
       return 0;
    }
    vector<Point2f> bezierCurve(vector<Point2f> src)
    {
       if (src.size() < 1)//这种情况是不允许出现的,出现只能证明程序出错了
           return src;
       const float step = 0.01;//采集100个点,即1.0/step
       vector<Point2f> res;
       if (src.size() == 1) {//递归结束条件,k=0
           for (float t = 0; t < 1; t += step)
               res.push_back(src[0]);//为了和其他情况保持一致,生成了1.0/step个一样的点
           return res;
       }
       vector<Point2f> src1;
       vector<Point2f> src2;
       src1.assign(src.begin(), src.end() - 1);//分成两部分,即Pi和Pi+1
       src2.assign(src.begin() + 1, src.end());
       for (int i = 0; i < src1.size(); i++)
           cout << src1[i] << endl;
       cout << endl;
       for (int i = 0; i < src2.size(); i++)
           cout << src2[i] << endl;
       cout << endl;
       vector<Point2f> pln1 = bezierCurve(src1);
       vector<Point2f> pln2 = bezierCurve(src2);
       for (float t = 0; t < 1; t += step) {
           Point2f temp;
           temp = (1.0 - t) * pln1[cvRound(1.0 / step * t)] + t * pln2[cvRound(1.0 / step * t)];
           res.push_back(temp);
       }
       return res;
    }
    
    

    实现效果

    贝塞尔曲线

    展开全文
  • 计算机图形学:贝塞尔曲线(Bezier)的实现前言关于Bezier曲线的理解三级目录 前言 初次学习这个Bezier曲线的时候,觉得课本的一些介绍乱七八糟,本着便于大家理解的原则,我就从便于大家理解的角度——递推算法角度...

    计算机图形学:C++贝塞尔曲线(Bezier)的实现

    前言

    初次学习这个Bezier曲线的时候,觉得课本的一些介绍乱七八糟,本着便于大家理解的原则,我就从便于大家理解的角度——递推算法角度介绍Bezier曲线。如果想通过这篇文章理解Bernstein基函数的,可以绕路了。
    结尾附实现源码(vs2019 C++ EaxyX环境)

    关于Bezier曲线的理解

    首先需要简单了解一下Bezier曲线为什么要被发明出来——因为需要计算机绘制曲线,而没有系统有效的方法绘制出来,所以就有人研究出来了这个东西。(本人是这么理解的。。。)
    那么,怎么有效画出来?其实就是下面两张ppt的事。
    在这里插入图片描述

    这个过程其实是动态的,
    P 0 P 0 1 P 0 P 1 = t \tfrac{P_0P_0^1}{P_0P_1}=t P0P1P0P01=t

    P 1 P 1 1 P 1 P 2 = t \tfrac{P_1P_1^1}{P_1P_2}=t P1P2P1P11=t

    P 0 1 P 0 2 P 0 1 P 1 1 = t \tfrac{P_0^1P_0^2}{P_0^1P_1^1}=t P01P11P01P02=t

    t ϵ [ 0 , 1 ] t\epsilon[0,1] tϵ[0,1]
    t从0逐渐递增到1,这个过程完成,就成了我们所看到的曲线。
    当然,这其实是最简单的Bezier曲线,这里大家可以思考一下,如果 P 2 P_2 P2向左下角延伸出一条线段 P 2 P 3 P_2P_3 P2P3,那么这个绘制过程会怎么样?
    思考
    结果
    这个结果图我画的简单了点,对应的点没有标出来,读者自行体会。
    同样,t的从0到1的递增能够画出了Bezier曲线
    在这里插入图片描述
    这张图就提供了怎么转成可执行源码的思路了。
    我个人觉得递推算法是才是能够理解Bezier曲线的途径。

    可执行程序

    #include <iostream>
    #include <cstdio>
    #include <graphics.h>
    #include <cmath>
    using namespace std;
    class Point//简单写的一个Point的数据结构
    {
    public:
    	int x, y;
    	Point(int x1, int y1)
    	{
    		x = x1;
    		y = y1;
    	}
    	Point()
    	{
    		x = 0;
    		y = 0;
    	}
    	void Set(int x1, int y1)
    	{
    		x = x1;
    		y = y1;
    	}
    };
    int main()
    {
    	initgraph(1000, 1000);
    	//简单画出一个六边形
    	line(250, 50, 750, 50);
    	line(750, 50, 1000, 300);
    	line(1000, 300, 750, 550);
    	line(750, 550, 250, 550);
    	line(250, 550, 0, 300);
    	line(0, 300, 250, 50);
    	
    	//把相应点初始化
    	Point P[7];
    	P[0].Set(250, 50);
    	P[1].Set(750, 50);
    	P[2].Set(1000, 300);
    	P[3].Set(750, 550);
    	P[4].Set(250, 550);
    	P[5].Set(0,300);
    	P[6].Set(250, 50);
    	int n = 4;//P[0]-P[n-1]参与绘制Bezier曲线
    	Point PO;
    	double t;
    	for (t = 0; t < 1; t += 0.000001)
    	{
    		//每一次运算都要重新赋一次初始值
    		P[0].Set(250, 50);
    		P[1].Set(750, 50);
    		P[2].Set(1000, 300);
    		P[3].Set(750, 550);
    		//参照n的值,一直赋初始值到P[n-1]
    		
    		int x = n;
    		while (1)
    		{
    			if (x == 1)
    				break;
    			for (int i = 0; i < x - 1; i++)
    			{
    				PO.x = ((1 - t) * P[i].x + t * P[i + 1].x);
    				PO.y = ((1 - t) * P[i].y + t * P[i + 1].y);
    				P[i] = PO;
    			}
    			x--;
    		}
    		putpixel(P[0].x, P[0].y, RED);
    		//计算出比例系数为t时对应的Bezier曲线上的点,画出该点。
    	}
    	getchar();
    }
    

    执行结果:
    在这里插入图片描述
    程序写的比较原始,没有加工,没有写得规律很普适,但是简单理解应该是没问题了。

    展开全文
  • 用了两种算法,公式法和递归算法 界面用OpenGL做的 刚学,里面还有B样条没完成,哪个高手做了在把它传上来给我学习一下 谢谢 O(∩_∩)O~
  • 三阶贝塞尔曲线只能计算近似解,由于使用时对长度的精度要求不高,因此用博客 【Unity】贝塞尔曲线关于点、长度、切线计算在 Unity中的C#实现 中提供的C#方法改写为C++的,只是替换了一个结构体,因为并不懂原文中的...
  • 贝塞尔曲线

    2017-12-24 22:06:28
    vs结合easyX实现贝塞尔曲线的绘制,并可以通过鼠标拖拽控制点实现曲线的变化
  • 用TC生存二次贝塞尔曲线,其实稍微改一下就可以实现三次贝塞尔曲线的了。
  • 通过重写paintEvent函数来实现画贝塞尔,同时可在界面上通过鼠标点击来绘制/选择节点,并且可拖动节点调节位置
  • 废话不说,直接上代码: ... * 三次方贝塞尔曲线由四个点定义: 起点、终点 和两个控制点。 * @param rawPointVector [in]原始点数组 * @param firstControlPointVector [out]第一个控制点,总...
  • 基于OPENGL的贝塞尔曲线生成算法,运行时在白色区域时设立四个特征点,即可画出相应的贝塞尔曲线
  • C++ 生成三次贝塞尔曲线

    千次阅读 2013-06-08 11:21:54
    // 三次贝塞尔.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #define NUM_STEPS 30 //越大,曲线越密,越逼近 using namespace std; class ...
  • 实现C++ opengl的glut库接口函数画贝塞尔曲线和均匀B样条曲线
  • 图形算法:贝塞尔曲线

    万次阅读 多人点赞 2016-11-03 10:28:22
    图形算法:贝塞尔曲线标签(空格分隔): 算法版本:0 作者:陈小默 声明:禁止商用,禁止转载 发布于:作业部落、CSDN博客图形算法贝塞尔曲线 一什么是贝塞尔曲线贝塞尔曲线的数学推导过程 三使用CC语言实现...
  • 贝赛尔曲线C++实现

    2018-05-30 14:56:18
    三次贝塞尔曲线C++实现,附有曲线图,完整的程序编码。
  • 基于OpenGL的贝塞尔曲线算法的实现,可交互,可以使用鼠标依次选取坐标点。
  • matlab源码,实现1-8阶贝塞尔(bezier)曲线拟合。另外附了一个拟合后的评价标准,sse,rmse等的说明(感谢hitwyb)
  • 此文档详细描述了如何去用C++绘制一个贝塞尔曲线的过程源码
  • C++ 多控制点生成贝塞尔(Bezier-Curve)样条 C 语言版。
  • 该资源下载下来即可以运行,输入的散点大于0小于32,这都是自己可以扩充的
  • 实现贝塞尔曲线的绘制,并可以移动控制点来改变曲线形状
  • C++贝塞尔曲线,可以在任意平台编译,可移植的贝塞尔曲线 贝塞尔曲线例子 Bezier demo bezier curve writen by qt5.8 qt5.8 project of bezier, you can open the pro file and compile the project directly it ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,622
精华内容 648
关键字:

贝塞尔曲线c++

c++ 订阅