码迷,mamicode.com
首页 > 其他好文 > 详细

贝塞尔曲线

时间:2016-05-12 17:18:36      阅读:522      评论:0      收藏:0      [点我收藏+]

标签:

概述

贝塞尔曲线在android中运用广泛,可以用来绘制各类复杂曲线,因为贝塞尔曲线只需要指定控制点,就能绘制出特定的曲线。其次是做点和点的平滑过渡。为什么可以做到如上两点,看下面的讲解:

技术分享

首先来说,贝塞尔曲线有阶的概念,这个阶可以理解为控制点,一阶的控制点只有两个。如上是一阶的方程,其中t取值为0到1,可以理解为时间,从开始到结束。动图如下:

技术分享

图中可以看到,点随着t的变化从p0到p1运动,一阶的贝塞尔其实就是一条直线。

那么二阶又是如何呢:

技术分享

方程如上,可以通过一阶方程推导出来,这里不做演算,看看动图:

技术分享

动图中可以看到,有3个控制点。在控制点p0到p1之间有一个点随着t变化而运动,在p1到p2之间也有点随着变动,他们两个点遵循一阶方程,而两个点的连线上又有一个点随着t运动,这个点也遵循一阶方程,所以可以推导出上诉方程。

再来看看三阶方程和动图:

技术分享

!技术分享

接下来是四阶和五阶的贝塞尔曲线动图:

技术分享

技术分享

从上面的图中可以看出:
1. 贝塞尔曲线,可以由控制点来控制曲线的走势。
1. 拐点处,曲线可以平滑过渡

由以上两点结论,可以看出,贝塞尔曲线可以用来方便简单的绘制各类曲线,同时,可以用于平滑处理拐点。后面将讲解示例。

方法

//二阶  
public void quadTo(float x1, float y1, float x2, float y2)  
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)  
//三阶  
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)  
public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3) 

在Path类中有如上四个方法是有关于贝塞尔曲线的,其中前面两个是二阶,后面两个是三阶。

这里quadTo以及cubicTo方法都是直接接收控制点的坐标,而rQuadTo以及rCubicTo接收的是相对坐标,即相对上一个控制点的相对坐标

例如:

path.moveTo(100,300);  
path.rQuadTo(100,-100,200,0);  
path.rQuadTo(100,100,200,0);  

等价于

path.moveTo(100,300);  
path.quadTo(200,200,300,300);  
path.quadTo(400,400,500,300);

实例

假设我们有四个个数据点,需要绘制出他们组成的连线,数据点数据为{200, 500},{400, 700}{600, 300}{800, 1000}。那么绘制出来的结果如下:

技术分享

这就是四个点依次连接所组成的连线,看起来是没有问题的。如果,这里有一个需求,需要将这个折线变成平滑的曲线,那么我们应该怎么做呢。从上面讲解的知识知道,折线的过渡,可以使用贝塞尔曲线。

贝塞尔曲线的核心在于找到合适的控制点,因为控制点中,曲线一定会经过首尾两个点,那么图中的四个点,应该分别为首尾控制点。

先看一下最终绘制的曲线:

技术分享

看起来确实平滑了,这四个红色的点就是上面的数据点,根据上面的结论,要让曲线经过他们,他们必须相邻的两个点为首尾控制点,那么这里使用几阶的贝塞尔合适呢。

我们以第二和第三两个点作为入手点(因为任意两个点是控制点的首尾,属于同一组控制点),要让曲线从点1平滑过渡到点2,再从点3平滑过渡到点四,那么这曲线的走势必然要受到点1,点4的影响,也就是说,两个相邻数据点之间的曲线走势,不仅受到自己的所在位置的影响,还受到前后两点的影响,加起来其实影响这段曲线走势的就有四个点。那么可以推断出来,四个点的贝塞尔曲线,当然是三阶曲线。

那么既然相邻两个数据点是控制点的首尾点,那么另外两个控制点又该如何求得呢,从上面的分析可以知道,另外两个控制点,是为了控制曲线的走势。我们先来完整控制点的图:

技术分享

图中绿色的为控制点,红色的为数据点,每个数据点实际上也是控制点之一(只是首尾点),因此数据点既有红色也有绿色。可以看到点2和点3之间的两个控制点,一个靠近点2,一个靠近点3,靠近点2的控制点比点2高,靠近点3的控制点也比点3高,这是为什么呢。

技术分享

先看上图,将靠近点2和点3的两个控制点分别标识为点a,和点b。

现在的问题在于,如何求的点a和点b的坐标。

点a,点b作为数据点2,和3之间的控制点,他们的作用,其实是为了体现前后数据点的连线趋势。先来看点a,点a要起到的作用就是要顺利的让数据点2平滑的过渡从数据点1来的曲线,并且要将曲线平滑的过渡到数据点3,那么点a相关的点就应该有数据点1,数据点2,以及数据点3。也就是说,点a的值应该由这3个点求出。同理,点b也会由他临近的数据点,以及该数据点前面的数据点和后面的数据点求得。也就是数据点3,数据点2,数据点4求得。

代码如下:

final float LINE_SMOOTHNESS = 0.2f;
final float firstDiffX = (currentPointX - prePreviousPointX);
final float firstDiffY = (currentPointY - prePreviousPointY);
final float secondDiffX = (nextPointX - previousPointX);
final float secondDiffY = (nextPointY - previousPointY);
final float firstControlPointX = previousPointX + (LINE_SMOOTHNESS * firstDiffX);
final float firstControlPointY = previousPointY + (LINE_SMOOTHNESS * firstDiffY);
final float secondControlPointX = currentPointX - (LINE_SMOOTHNESS * secondDiffX);
final float secondControlPointY = currentPointY - (LINE_SMOOTHNESS * secondDiffY);
path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,currentPointX, currentPointY);

代码中,最后的path.cubicTo就是调用了四阶贝塞尔曲线的方法。其中firstControlPointX对应的是点a的x坐标,secondControlPointX对应的是点b的x坐标,currentPointX对应数据点3的x坐标。previousPointX对应了前一个数据点也就是数据点2的x坐标,prePreviousPointX对应了数据点1,nextPointX对应了数据点4。

以上的代码可以看出,点a,点b的相关点与我们之前分析的一致。那么这里除了这些数值以外,还有一个变量LINE_SMOOTHNESS,这个是做什么的呢。从它的名字可以看出,他是调整曲线的平滑程度的,值变化会引起控制点a,b的变化,从而影响曲线的形态。

final float LINE_SMOOTHNESS = 0.4f;

来看看值调整为0.2,和0.4的对比曲线:左边为0.2右边为0.4,可以根据具体需求调整值

技术分享 技术分享

贝塞尔曲线

标签:

原文地址:http://blog.csdn.net/cquwentao/article/details/51365797

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!