如何通过几个点来canvas绘制贝塞尔曲线线

关于贝塞尔曲线,网上很多博客都已经给出了解释。。。真的好多。
但是我看了几百遍都不明白!!不知道大家跟我有没有同样的感受。所以就来个重点解释,通俗易懂版给大家吧~~
下面的这个图,相信你也看到过很多。然而,我这里也是需要贴一下这个图的(不知道是哪个大神的图,不好意思,借用一下)。
P0是曲线的开始点
P3是曲线的结束点
P1和P2是控制曲线走势的控制点,所以这两个点事实上是辅助作用,并不会在画布中被绘制出来
t参数重点讲解
t是辅助参数,可以看到它的值范围是[0,1]。这个t值作用于图中的所有直线(P0P1、P1P2、P2P3、两条绿线、蓝线)。
注意:图中真实绘制出来的,就只有红线,其他的都只是辅助的,并不会被真实绘制出来。
在上图中,你可以看成这个三次贝塞尔曲线由两个二次贝塞尔曲线组成。1.P0P1P2组成的二次贝塞尔曲线 & & 2.P1P2P3组成的二次贝塞尔曲线。
所以要把上图的三次贝塞尔曲线拆分成两个二次贝塞尔曲线讲解
对于第一个二次贝塞尔曲线P0P1P2,
当t=0.5的时候(其实就是[0,1]的中间值,这个比较好理解),情况应该是这样的:
1.找到P0P1线(方向P0-&P1,这是有方向的线段)的50% (因为t=0.5,即0.5*100%) 的位置,标上一个绿色的点
2.同1步骤,在P1P2线上的50%位置上标上一个绿色的点
3.把步骤1和2的绿色点连成一条线
4.然后在这条绿色的50%位置处标上一个红点,这个红点就是实际绘制的曲线中的一个点。(当t值不断变化,就会出现不同位置的红点,组成一条曲线)
第二个二次贝塞尔曲线的理解跟第一个二次贝塞尔曲线一样!
看到这里你也许会问:设计师给了设计稿给我,怎么才能把设计稿里面的曲线还原出来?!
在PS里面,画弧线使用钢笔工具的,所以跟我们的原理是一样的,只要设计师给出开始点,结束点,控制点就OK啦~~~
阅读(...) 评论() &主题信息(必填)
主题描述(最多限制在50个字符)
申请人信息(必填)
申请信息已提交审核,请注意查收邮件,我们会尽快给您反馈。
如有疑问,请联系
互联网运营,专注打杂30年。
逝去了青春至少还留下了代码
学习编程,改变世界
给出了Swift和Objective-C两版开源方案,仅需给出若干关键点,即可绘制出一条「经过」这些关键点的光滑曲线,且关键点位置、曲度参数皆实时可调。Swift版GitHub地址:Objective-C版:> 博客详情
参数方程表现形式
在中学的时候,我们都学习过直线的参数方程:y = kx +其中k表示斜率,b表示截距(即与y轴的交点坐标)。类似地,我们也可以用一个参数方程来表示一条曲线。1962年,法国工程师贝塞尔发明了贝塞尔曲线方程。关于贝塞尔曲线的详细介绍可以参考(维基)。这里只介绍OpenGL实现贝塞尔的函数。
OpenGl定义一条曲线时,也把它定义为一个曲线方程。我们把这条曲线的参数成为u,它的就是曲线的。曲面则需要u和v两个参数来描述。注意,u和v参数只表示了描述曲线的参数方程的范围,它们并没有反映实际的坐标值。其坐标可以表示为:
x = f(u); y = g(u); z = h(u);
贝塞尔曲线的形状由控制点来控制。贝塞尔曲线的控制点个数为曲线的阶。根据控制点的个数,贝塞尔曲线又分为二次贝塞尔曲线,三次贝塞尔曲线,高阶贝塞尔曲线。
线性贝塞尔曲线演示动画,t in [0,1]
二次方曲线
为建构二次贝塞尔曲线,可以中介点Q0和Q1作为由0至1的t:
由P0至P1的连续点Q0,描述一条线性贝塞尔曲线。
由P1至P2的连续点Q1,描述一条线性贝塞尔曲线。
由Q0至Q1的连续点B(t),描述一条二次贝塞尔曲线。
二次贝塞尔曲线的结构 二次贝塞尔曲线演示动画,t in [0,1]
三次方曲线
为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:
三次贝塞尔曲线的结构 三次贝塞尔曲线演示动画,t in [0,1]
两段曲线是否相连接,代表这两段曲线是否连续的。曲线的连续性分为4种,无连续,点连续,正切连续,曲率连续。下图分别表示了这几种情况:
其中曲率连续的曲线过渡的更平滑。我们可以通过参数来设置曲线的连续性。
OpenGL提供了一些函数来绘制贝塞尔曲线和曲面。我们只需要提供控制点和u,v作为参数,然后调用求值函数来绘制曲线。
2D曲线的例子:
//控制点&GLint&numOfPoints&=&4;&static&GLfloat&controlPoints[4][3]&=&{{-4.0f,&0.0f,&0.0f},
{-6.0f,&4.0f,&0.0f},
{6.0f,&-4.0f,&0.0f},
{4.0f,&0.0f,&0.0f}};&void&SetupRC()
&&glClearColor(0.0f,&0.0f,&0.0f,&1.0f);
&&glColor3f(1.0f,&0.0f,&1.0f);
//画控制点
void&DrawPoints()
&&glPointSize(2.5f);
&&glBegin(GL_POINTS);&for&(int&i&=&0;&i&&&numOfP&++i)
&&&&&&glVertex3fv(controlPoints[i]);
&&glEnd();
void&ChangeSize(GLsizei&w,&GLsizei&h)
&&if&(h&==&0)
&&&&h&=&1;
&&glViewport(0,&0,&w,&h);
&&&//使用正交投影
&&glMatrixMode(GL_PROJECTION);
&&glLoadIdentity();
&&gluOrtho2D(-10.0f,&10.0f,&-10.0f,&10.0f);
&&glMatrixMode(GL_MODELVIEW);
&&glLoadIdentity();
void&RenderScene()
&&glClear(GL_COLOR_BUFFER_BIT);
&&&//设置贝塞尔曲线,这个函数其实只需要调用一次,可以放在SetupRC中设置
&&&&glMap1f(GL_MAP1_VERTEX_3,&//生成的数据类型
&&&&&0.0f,&//u值的下界
&&&&&&100.0f,&//u值的上界
&&&&&&&3,&//顶点在数据中的间隔,x,y,z所以间隔是3
&&&&&&&&numOfPoints,&//u方向上的阶,即控制点的个数
&&&&&&&&&&controlPoints[0][0]&//指向控制点数据的指针&);
&&&//必须在绘制顶点之前开启
&&&glEnable(GL_MAP1_VERTEX_3);
&&&&//使用画线的方式来连接点
&&&glBegin(GL_LINE_STRIP);
&&for&(int&i&=&0;&i&&=&100;&i++)
&&&&glEvalCoord1f((GLfloat)i);
&&glEnd();
&&DrawPoints();
&&glutSwapBuffers();
在RenderScene函数中调用glMap1f来为曲线创建映射。第一个参数为GL_MAP1_VERTEX3,设置求值器产生顶点为三元组(x,y,z).还可以设置为产生纹理坐标和颜色信息。参考.后面的两个参数设定了u的取值范围[0,100],第四个参数指定了顶点在数组中的间隔,由于顶点是由3个浮点数组成,所以间隔是3.第五个参数指定了控制点的个数,最后一个参数是控制点数组。然后我们需要启用求值器,调用如下:
glEnable(GL_MAP1_VERTEX3);
glEvalCoord1f函数,接受一个参数为曲线的参数值。调用这个函数会通过求值函数求出顶点坐标值,然后内部调用了glVertex。这里使用连线的方式来连接这些顶点:
glBegin(GL_LINE_STRIP);
for(i = 0; I &= 100; i++)
& glEvalCoord1f((GLfloat)i);
OpenGl还提供了更简单的方式来完成上面的任务。我们可以通过glMapGrid函数来设置一个网格,来告诉OpenGL在u的值域的范围内创建一个包含各个点的空间对称的网格。然后,我们调用glEvalMesh,使用指定的图元(GL_LINE或GL_POINTS)来链接各个点。
我们用下面的两个函数调用
&&glMapGrid1f(100,&0.0f,&100.0f);
&&glEvalMesh1(GL_LINE,&0,&100);
可以替换下面的代码
glBegin(GL_LINE_STRIP);&
for&(int&i&=&0;&i&&=&100;&i++)
&&&&glEvalCoord1f((GLfloat)i);
使用这种方式更为紧凑。
创建一个贝塞尔曲面与创建一个贝塞尔曲线类似。除了给出u的定义域之外,还要给出v的定义域。下面的例子是创建一个贝塞尔曲面。与之前不同的是,我们沿着v的定义域定义了3组控制点。为了保持曲面的简单,这几组控制点只是z值不同。用这种方式画的曲面,看起来像是曲线沿z轴的扩展。
//控制点&&GLint&nNumPoints&=&3;
GLfloat&ctrlPoints[3][3][3]=&{{{&&-4.0f,&0.0f,&4.0f},&&&&
{&-2.0f,&4.0f,&4.0f},&&&&
{&&4.0f,&0.0f,&4.0f&}},
{{&&-4.0f,&0.0f,&0.0f},&&&&
{&-2.0f,&4.0f,&0.0f},&&&&
{&&4.0f,&0.0f,&0.0f&}},
{{&&-4.0f,&0.0f,&-4.0f},&&&&
{&-2.0f,&4.0f,&-4.0f},&&&&
{&&4.0f,&0.0f,&-4.0f&}}};&//画控制点&&void&DrawPoints(void)
{&int&i,j;&&&&
&&glColor3f(1.0f,&0.0f,&0.0f);&//把点放大一点,看得更清楚&&glPointSize(5.0f);
&&glBegin(GL_POINTS);&
&&for(i&=&0;&i&&&nNumP&i++)
&&&for(j&=&0;&j&&&3;&j++)
&&&&&&glVertex3fv(ctrlPoints[i][j]);
&&glEnd();
void&RenderScene(void)
//&Clear&the&window&with&current&clearing&color&
&&glClear(GL_COLOR_BUFFER_BIT&|&GL_DEPTH_BUFFER_BIT);
&&&//&保存模型视图矩阵&&
&&&glMatrixMode(GL_MODELVIEW);
&&glPushMatrix();&
&&//旋转一定的角度方便观察&&
&&glRotatef(45.0f,&0.0f,&1.0f,&0.0f);
&&glRotatef(60.0f,&1.0f,&0.0f,&0.0f);
&&glColor3f(0.0f,&0.0f,&1.0f);&//设置映射方式,只需要设置一次可以在SetupRC中调用。&&
&&glMap2f(GL_MAP2_VERTEX_3,&//生成的数据类型&&
&&0.0f,&//&u的下界&
&&10.0f,&//u的上界&&
&&3,&//数据中点的间隔&&
&&3,&//u方向上的阶&&
&&0.0f,&//v的下界&&
&&10.0f,&//v的上界&&
&&9,&//&控制点之间的间隔&&
&&3,&//&v方向上的阶&&
&&&ctrlPoints[0][0][0]);&//控制点数组&
&&//启用求值器&&
&&glEnable(GL_MAP2_VERTEX_3);&
&&//从0到10映射一个包含10个点的网格&&
&&glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);&
&&//&计算网格&&
&&glEvalMesh2(GL_LINE,0,10,0,10);&
&&//画控制点&&
&&DrawPoints();
&&glPopMatrix();
&&glutSwapBuffers();
在这里我们用glMap2f替换了之前的glMap1f, 这个函数指定了u和v两个域上的点。除了指定u的上界和下界之外,还要指定v的上界和下界。v定义域内点的距离是9,因为这里使用了3维数组,包含了3个u值,每个u值又包含了3个点,3x3=9。然后指定v方向上的阶,即每个u分支上v方向有多少个点。最后一个参数是指向控制点的指针。
然后我们设置求值器.
//启用求值器
glEnable(GL_MAP2_VERTEX_3); //从0到10映射一个包含10个点的网格
glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);
计算网格网格表面,用线的方式表示。
// 计算网格 & glEvalMesh2(GL_LINE,0,10,0,10);
光照和法线
求值器还可以帮我们生成表面的法线,只需简单的修改一些代码:
把glEvalMesh2(GL_LINE, 0, 10, 0, 10);替换为glEvalMesh2(GL_FILL, 0, 10, 0, 10);然后在初始化时 SetupRC中调用glEnable(GL_AUTO_NORMAL);就可以得到一个收到光照的曲面了。
人打赏支持
参与源创会
领取时间:
“”在线下联结了各位 OSCer,推广开源项目和理念,很荣幸有你的参与~
领取条件:参与过开源中国“源创会”的 OSCer 可以领取
码字总数 97096
支付宝支付
微信扫码支付
打赏金额: ¥
已支付成功
打赏金额: ¥VC &&&&最新内容
VC &&&&随机内容

我要回帖

更多关于 canvas绘制贝塞尔曲线 的文章

 

随机推荐