概念
贝塞尔曲线是用一系列点来控制曲线状态的,我们将这些点简单分为两类,一类是控制点,一类是数据点。
1、一阶贝塞尔曲线
一阶贝塞尔曲线没有控制点,只有两个数据点。最终的效果为一条直线。

2、二阶贝塞尔曲线
由两个数据点(点A和点C)和一个控制点(点B)来描述曲线状态。

3、三阶贝塞尔曲线
由两个数据点和两个控制点来描述曲线状态。

计算方法

在计算贝塞尔公式时,用到了德卡斯特利奥算法:
//其中,i为阶数,j为第几个点,t为时间(即走过的路程,0~1之间)
p(i, j) = (1 - t) * p(i - 1, j) + t * p(i - 1, j + 1)
在编写代码计算时,可以利用递归进行计算。
class BezierUtils(private var points: List<PointF>) {
/**
* 德卡斯特利奥算法,计算贝塞尔曲线
* @param i 贝塞尔曲线阶数
* @param j 控制点
* @param t 时间
* @return
*/
fun deCasteljauX(i: Int, j: Int, t: Float): Float {
if (i == 1) {
return (1 - t) * points[j].x + t * points[j+1].x
}
return (1-t) * deCasteljauX(i-1,j,t) + t*deCasteljauX(i-1,j+1,t)
}
/**
* 德卡斯特利奥算法,计算贝塞尔曲线
* @param i 贝塞尔曲线阶数
* @param j 控制点
* @param t 时间
* @return
*/
fun deCasteljauY(i: Int, j: Int, t: Float): Float {
if (i == 1) {
return (1 - t) * points[j].y + t * points[j+1].y
}
return (1-t) * deCasteljauX(i-1,j,t) + t*deCasteljauX(i-1,j+1,t)
}
}
一个简单的绘制任意阶贝塞尔曲线的Demo:
class BezierView(context: Context?, attrs: AttributeSet?) : BaseView(context, attrs) {
lateinit var points: ArrayList<PointF>
lateinit var path: Path
var bezierUtils: BezierUtils
init {
init()
bezierUtils = BezierUtils(points)
}
fun init() {
points = arrayListOf()
var order = 5
for (i in 1..order) {
var p = PointF()
p.x = (100f + Math.random() * 800f).toFloat()
p.y = (100f + Math.random() * 800f).toFloat()
points.add(p)
}
path = Path()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var i = 0
paint.color = Color.GRAY
for (p in points) {
if (i == 0) {
path.moveTo(p.x, p.y)
} else {
path.lineTo(p.x, p.y)
}
I++
canvas?.drawCircle(p.x, p.y, 10f, paint)
}
canvas?.drawPath(path, paint)
paint.color = Color.RED
drawBezier()
canvas?.drawPath(path, paint)
path.reset()
}
private fun drawBezier() {
path.reset()
bezierUtils.setPoints(points)
var t = 0f
for (i in 0..1000) {
if (i == 0) {
path.moveTo(points[0].x, points[0].y)
}
t = (i * 0.001).toFloat()
var x = bezierUtils.deCasteljauX(points.size - 1, 0, t)
var y = bezierUtils.deCasteljauY(points.size - 1, 0, t)
path.lineTo(x, y)
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event!!.action == MotionEvent.ACTION_DOWN) {
init()
invalidate()
}
return super.onTouchEvent(event)
}
}
class BezierUtils(private var points: List<PointF>) {
fun setPoints(points: List<PointF>) {
this.points = points
}
/**
* 德卡斯特利奥算法,计算贝塞尔曲线
* @param i 贝塞尔曲线阶数
* @param j 控制点
* @param t 时间
* @return
*/
fun deCasteljauX(i: Int, j: Int, t: Float): Float {
if (i == 1) {
return (1 - t) * points[j].x + t * points[j + 1].x
}
return (1 - t) * deCasteljauX(i - 1, j, t) + t * deCasteljauX(i - 1, j + 1, t)
}
/**
* 德卡斯特利奥算法,计算贝塞尔曲线
* @param i 贝塞尔曲线阶数
* @param j 控制点
* @param t 时间
* @return
*/
fun deCasteljauY(i: Int, j: Int, t: Float): Float {
if (i == 1) {
return (1 - t) * points[j].y + t * points[j + 1].y
}
return (1 - t) * deCasteljauY(i - 1, j, t) + t * deCasteljauY(i - 1, j + 1, t)
}
}

网友评论