贝塞尔曲线
2019-11-06 本文已影响0人
放肆滴微笑
贝塞尔曲线的来历
贝塞尔曲线的数学基础是早在 1912 年就广为人知的[伯恩斯坦多项式。直到1959年。当时就职于雪铁龙的法国数学家 Paul de Casteljau 开始对伯恩斯坦多项式进行了图形化的尝试,并且提供了一种数值稳定的德卡斯特里奥(de Casteljau) 算法。(多数理论公式是建立在大量且系统的数学建模基础之上研究的规律性成果)根据这个算法,就可以实现 通过很少的控制点,去生成复杂的平滑曲线,也就是贝塞尔曲线。
但贝塞尔曲线的声名大噪,不得不提到1962年就职于雷诺的法国工程师皮埃尔·贝塞尔(Pierre Bézier),他使用这种方法来辅助汽车的车体工业设计(最早计算机的诞生则是为了帮助美国海军绘制弹道图),并且广泛宣传(典型的理论联系实际并获得成功的示例),因此大家称为贝塞尔曲线。
贝塞尔曲线的数学理论
贝塞尔曲线的本质是通过数学计算公式去绘制平滑的曲线,这些曲线是用一系列点来控制曲线状态的,可以将这些点分为2类,一类是数据点,一类是控制点。
N 阶贝塞尔
可以理解为有N+1个点 N+1减1 就是 N阶贝塞尔,比如3阶就是4个点链接的线,4-1=3,就叫做3阶贝塞尔
一阶贝塞尔
image可以看到一阶贝塞尔是一条直线,可以通过几何知识得到t的坐标点
image.png
二阶贝塞尔
-
步骤一:在平面上选择3个不同的点用线依次链接
image.png -
步骤二:在AB和BC线段上找对点D和E,并且AD/AB = BE/BC
-
步骤三:链接DE,在DE上选择点F,F点满足DF/DE=AD/AB = BE/BC
image -
步骤四:根据DE线段和计算公式找出所有的F点,然后将F点链接起来。连接规则比如上图,A-F..F1..F2..C,F点必须满足DF/DE = AD/AB=BE/BC
image
Path中贝塞尔曲线的实现
二阶贝塞尔
起点为之前的结束点,或者是下笔点
4个参数,x1,y1,为控制点,也就是B点,x2,y2为终点
public void quadTo(float x1, float y1, float x2, float y2)
Path path = new Path();
path.moveTo(100,100); // 起点
path.quadTo(400,200,50,500); //控制点和终点
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawPath(path,paint1);
image.png
三阶贝塞尔
起点为之前的结束点,或者是下笔点
x1,y1第一个控制点,x2,y2第二个控制点,x3,y3为终点
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3)
Path path = new Path();
path.moveTo(100,100);
path.cubicTo(400,200,50,500,400,400);
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawCircle(400,400,10,paint);
canvas.drawPath(path,paint1);
image.png
N 阶
//生成贝塞尔Path
private ArrayList<PointF> buildBezierPoints() {
mPath.reset();
ArrayList<PointF> points = new ArrayList<>();
int order = controlPoints.size() - 1;//阶数
//份数
float delta = 1.0f / 1000;
for (float t = 0; t <= 1; t += delta) {
//bezier点集
PointF pointF = new PointF(deCastelJau(order, 0, t, true), deCastelJau(order, 0, t, false));//计算在曲线上点位置
points.add(pointF);
if (points.size() == 1) {
mPath.moveTo(points.get(0).x, points.get(0).y);
} else {
mPath.lineTo(pointF.x, pointF.y);
}
}
return points;
}
/**
* p(i,j) = (1-t) * p(i-1,j) + t * p(i-1,j+1);
*
* @param i 阶数
* @param j 控制点
* @param t 时间
* @param calculateX 计算哪个坐标值 true=x
* @return
*/
private float deCastelJau(int i, int j, float t, boolean calculateX) {
if (i == 1) {
return calculateX ? (1 - t) * controlPoints.get(j).x + t * controlPoints.get(j + 1).x :
(1 - t) * controlPoints.get(j).y + t * controlPoints.get(j + 1).y;
} else {
return (1 - t) * deCastelJau(i - 1, j, t, calculateX) + t * deCastelJau(i - 1, j + 1, t, calculateX);
}
}
PointF pointF;
for (int i = 0; i < controlPoints.size(); i++) {
pointF = controlPoints.get(i);
if (i > 0) {
paint.setColor(Color.GRAY);
canvas.drawLine(controlPoints.get(i - 1).x,
controlPoints.get(i - 1).y,
pointF.x, pointF.y, paint);
}
if (i == 0) {
//起点为红色
paint.setColor(Color.RED);
} else if (i == controlPoints.size() - 1) {
//终点为蓝色
paint.setColor(Color.BLUE);
}
canvas.drawCircle(pointF.x
, pointF.y, 10, paint); //画所有的控制点
}
buildBezierPoints(); //生成贝塞尔Path
canvas.drawPath(mPath, paint1); //画线
image.png