简单而粗暴的方法画任意阶数Bezier曲线

虽然说是任意阶数,但是嘞,算法原理是可以到任意阶数,计算机大概到100多阶就会溢出了

Bezier曲线介绍

本文代码

背景

在windows的OpenGL环境中,使用鼠标在屏幕上选点,并以点为基础画出Bezier曲线

  • 初始化
  • 鼠标操作
  • 3阶以内Bezier曲线
  • n阶Bezier曲线

初始化

创建窗口,初始化大小、显示模式、添加显示和鼠标等回调函数,设置背景颜色等。

完成之后,定义两个全局的int类型的vector 用于存储鼠标在窗口中选择的点。同时定义窗口的高度和宽度。

vector<int> x_loc = {};
vector<int> y_loc = {};
int height = 600;
int width = 600;
  • 定义画点函数
void drawPixel(double x, double y, int point_size)
{
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPointSize(point_size);
glBegin(GL_POINTS);
glVertex2d(x, y);
glEnd();
}

其中point_size为点的大小。

鼠标操作

OpenGL中存在鼠标点击、拖动等操作的回调函数,使用十分方便,调用即可。

我们定义在鼠标左键按下抬起后为一次屏幕选点,并将所选的点的坐标压入存储存储点的坐标的容器中。

void Mouse_hit(int button, int state, int x, int y)
{
/// state == 1 mean button up
/// state == 0 mean button down
/// button == 0 mean left button
/// button == 1 mean middle button
/// button == 2 mean right button
/// [x, y] is the location of mouse pointer
if (button == 0 && state == 1)
{
x_loc.push_back(x);
y_loc.push_back(y);
cout << "point location: " << x_loc[x_loc.size() - 1] << " " << y_loc[y_loc.size() - 1] << endl;
}
if (button == 2 && state == 1){
x_loc.clear();
y_loc.clear();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glutSwapBuffers();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glutSwapBuffers();
glutPostRedisplay();
cout<<" Clear Screen!"<<endl;
}
glutSwapBuffers();
glutPostRedisplay();
}
  • 回调函数使用为

    glutMouseFunc(Mouse_hit);
  • Mouse_hit函数中state代表当前鼠标的状态是按下还是抬起

  • button为按下的是左、中、右三键中的哪一个

  • [x, y]为当前鼠标指针的坐标。次坐标不是世界坐标系,使用时得进行转换,看后面

坐标转换

拿一张图简单说明一下。由于鼠标获取的是世界坐标系下的位置,而在屏幕上绘制点与线是使用的是当前绘图坐标系,所以要进行简单的坐标变换。

可在显示回调函数中使用如下代码重设OpenGL窗口。

	glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, height, 0);

好了,设置一下前景色和点的大小形状等,来看看画点的效果。

3阶以内的Bezier曲线

对于3阶以内的Bezier曲线,直接将Bezier曲线的定义公式展开,求解系数即可。

void drawBezier_1(vector<int> x, vector<int> y, int num_of_points)
{
float ax, bx;
float ay, by;
int temp_loc = x.size() - 2; glColor3f(0.0f, 0.0f, 1.0f);
drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7);
drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7);
ax = x[temp_loc + 0];
ay = y[temp_loc + 0];
bx = x[temp_loc + 1];
by = y[temp_loc + 1]; float t;
t = 0.0;
float dt = 0.002;
while (t <= 1)
{
float x_temp = (1 - t) * ax + t * bx;
float y_temp = (1 - t) * ay + t * by;
drawPixel(x_temp, y_temp, 1);
t += dt;
}
} void drawBezier_2(vector<int> x, vector<int> y, int num_of_points)
{
float ax, bx;
float ay, by;
float tSquared;
int temp_loc = x.size() - 3; ax = x[temp_loc + 0] - 2 * x[temp_loc + 1] + x[temp_loc + 2];
ay = y[temp_loc + 0] - 2 * y[temp_loc + 1] + y[temp_loc + 2];
bx = x[temp_loc + 0] * (-2) + x[temp_loc + 1] * 2;
by = y[temp_loc + 0] * (-2) + y[temp_loc + 1] * 2; glColor3f(0.0f, 0.0f, 1.0f);
drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7);
drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7);
drawPixel(x[temp_loc + 2], y[temp_loc + 2], 7); float t;
t = 0.0;
float dt = 0.002;
while (t <= 1)
{
tSquared = t * t;
float x_temp = ax * tSquared + bx * t + x[temp_loc + 0];
float y_temp = ay * tSquared + by * t + y[temp_loc + 0];
drawPixel(x_temp, y_temp, 1);
t += dt;
}
} void drawBezier_3(vector<int> x, vector<int> y, int num_of_points)
{
float ax, bx, cx;
float ay, by, cy;
float tSquared, tCubed;
int temp_loc = x.size() - 4; cx = 3.0 * (x[temp_loc + 1] - x[temp_loc + 0]);
bx = 3.0 * (x[temp_loc + 2] - x[temp_loc + 1]) - cx;
ax = x[temp_loc + 3] - x[temp_loc + 0] - cx - bx; cy = 3.0 * (y[temp_loc + 1] - y[temp_loc + 0]);
by = 3.0 * (y[temp_loc + 2] - y[temp_loc + 1]) - cy;
ay = y[temp_loc + 3] - y[temp_loc + 0] - cy - by; glColor3f(0.0f, 0.0f, 1.0f);
drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7);
drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7);
drawPixel(x[temp_loc + 2], y[temp_loc + 2], 7);
drawPixel(x[temp_loc + 3], y[temp_loc + 3], 7); float t;
t = 0.0;
float dt = 0.002;
while (t <= 1)
{
tSquared = t * t;
tCubed = tSquared * t;
float x_temp = (ax * tCubed) + (bx * tSquared) + (cx * t) + x[temp_loc + 0];
float y_temp = (ay * tCubed) + (by * tSquared) + (cy * t) + y[temp_loc + 0];
drawPixel(x_temp, y_temp, 1);
t += dt;
}
}
  • 对应阶数的函数都可实现,每过阶数+1个点画一次曲线。

n阶Bezier曲线

由Bezier的定义公式我们可以发现,画Bezier曲线需要求组合数 ,求组合数需要求阶乘,然后还需要求。因为c++中有求幂的函数,所以实现阶乘组合数即可。

  • 阶乘

    double fac(int n)
    {
    double result = 1;
    if (n == 0)
    return result;
    for (int i = 1; i <= n; i++){
    result *= i;
    }
    return result;
    }

    为了扩大计算范围,使用了double类型

  • 组合数

    double combinate(int n, int k)
    {
    if (k == 0)
    return 1;
    double result = 0;
    result = fac(n) / (fac(k)*(fac(n - k)));
    return result;
    }

    为了扩大计算范围,也使用了double类型,其中k <= n

  • n阶Bezier曲线

    void drawBezier(vector<int> x, vector<int> y, int num_of_points) {
    
    	float px = 0.0, py = 0.0; //point current should draw
    int n; //number of points -1
    float t = 0.0, dt = 0.0005; //t in [0, 1], dt is changes each time in t
    n = x.size() - 1;
    while (t <= 1) {
    for (int i = 0; i <= n; i++) {
    double temp = combinate(n, i)*powf(t, i)*powf(1 - t, n - i);
    px += temp * x[i];
    py += temp * y[i];
    }
    drawPixel(px, py, 1);
    t += dt;
    px = 0.0;
    py = 0.0;
    }
    }

效果

  • 1阶

  • 2阶

  • 3阶

  • n阶曲线画的❤

简单而粗暴。。。

简单而粗暴的方法画任意阶数Bezier曲线的更多相关文章

  1. Python:raschii库计算任意阶数Stokes波

    Stokes五阶波 最近发现一个很有用的Stokes波计算Python库,raschii官方说明,可以计算任意阶数,不同水深下的Stokes波,简单做了下测试,测试结果与脚本如下 Python 脚本 ...

  2. R语言:用简单的文本处理方法优化我们的读书体验

    博客总目录:http://www.cnblogs.com/weibaar/p/4507801.html 前言 延续之前的用R语言读琅琊榜小说,继续讲一下利用R语言做一些简单的文本处理.分词的事情.其实 ...

  3. CSharpGL(40)一种极其简单的半透明渲染方法

    CSharpGL(40)一种极其简单的半透明渲染方法 开始 这里介绍一个实现半透明渲染效果的方法.此方法极其简单,不拖累渲染速度,但是不能适用所有的情况. 如下图所示,可以让包围盒显示为半透明效果. ...

  4. 简单工厂VS工厂方法

    前言: GOF经典的23种设计模式在IT界现已被广为流传.由于比较长时间没有用了,个人对于不同模式与模式之间的区别也渐渐模糊,故开始重温设计模式的思想.也希望更给对设计模式感兴趣的朋友些许的启发. - ...

  5. Simple Factory vs. Factory Method vs. Abstract Factory【简单工厂,工厂方法以及抽象工厂的比较】

    I ran into a question on stackoverflow the other day that sort of shocked me. It was a piece of code ...

  6. 结合实例分析简单工厂模式&工厂方法模式&抽象工厂模式的区别

    之前写过一篇关于工厂模式(Factory Pattern)的随笔,里面分析了简单工厂模式,但对于工厂方法和抽象工厂的分析较为简略.这里重新分析分析三者的区别,工厂模式是java设计模式中比较简单的一个 ...

  7. Java设计模式之简单工厂、工厂方法和抽象工厂

    在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则 ...

  8. 最简单的 RabbitMQ 监控方法 - 每天5分钟玩转 OpenStack(158)

    这是 OpenStack 实施经验分享系列的第 8 篇. 先来看张图:这是 Nova 的架构图,我们可以看到有两个组件处于架构的中心位置:数据库和Queue.数据库保存状态信息,而几乎所有的 nova ...

  9. Java实现一个简单的加密解密方法

    Crypto是Java语言写的一个简单的加密解密方法. 使用方法: 加密方法 String cipherte=Enande.encrypt(content, pass): 解密方法 Enande.de ...

随机推荐

  1. Angular 文档中的修改链接是从哪里改的

    如何修改修改的文本的链接. 如下图表示的,如何修改这个地方的链接到自己的 SCM 中. 你需要修改的文件为: aio\tools\transforms\templates\lib\githubLink ...

  2. 不错的图表库:ChartDirector

    官网:http://www.advsofteng.com 1)for c++ 2)for .NET 3)for Java 4)for ASP/COM/VB 5)for PHP 6)for Python ...

  3. HDU 4725 The Shortest Path in Nya Graph (最短路 )

    This is a very easy problem, your task is just calculate el camino mas corto en un grafico, and just ...

  4. 自定义ItemDecoration设置分割线

    说道ItemDecoration不得不说三个方法: /** * @param c 画布 * @param parent RecyleView * @param state RecyclerView的当 ...

  5. C++入门经典-例2.10-控制输出精确度

    1:代码如下: // 2.10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...

  6. maven的依赖传递及冲突

    A->B(compile)     第一关系: a依赖b   compile B->C(compile)     第二关系: b依赖c   compile   当在A中配置 <dep ...

  7. JS基础_Null和Undefind

    1.Null Null类型的值只有一个值,就是null null专门用来表示一个为空的对象 var a=null; console.log(a);//nulltypeof a //object 2.U ...

  8. vim 配置 scala 语法高亮

    第一步:执行下面这个命令: mkdir -p ~/.vim/{ftdetect,indent,syntax}for d in ftdetect indent syntax ; do curl -o ~ ...

  9. SpringBoot上传文件临时失效问题

    线上的系统中不能上传文件了,出现如下错误: org.springframework.web.multipart.MultipartException: Could not parse multipar ...

  10. Spring配置多个数据源,并实现数据源的动态切换转载)

    1.首先在config.properties文件中配置两个数据库连接的基本数据.这个省略了 2.在spring配置文件中配置这两个数据源: 数据源1 <!-- initialSize初始化时建立 ...