贝塞尔曲线
贝塞尔曲线展示
一阶贝塞尔曲线
二阶贝塞尔曲线定义:DF:DE = AD:AB = BE:BC
三阶贝塞尔曲线
多阶贝塞尔曲线
三维贝塞尔曲线
贝塞尔曲线公式
P
(
t
)
=
(
1
−
t
)
3
P
1
+
3
t
(
1
−
t
)
2
P
2
+
3
t
2
(
1
−
t
)
P
3
+
t
3
P
4
P(t) = (1−t)^3P_1 + 3t(1-t)^2P_2 + 3t^2(1-t)P_3 + t^3P_4
P(t)=(1−t)3P1+3t(1−t)2P2+3t2(1−t)P3+t3P4
通过公式:n代表最高次数,i对应控制点顺序
B
i
n
(
t
)
=
n
!
i
!
(
n
−
i
)
!
t
i
(
1
−
t
)
n
−
i
B^n_i(t)= \frac{n!}{i!(n-i)!}t^i(1-t)^{n-i}
Bin(t)=i!(n−i)!n!ti(1−t)n−i
特性
1、一个贝塞尔曲线可分割成多个,且不影响整个曲线,不提升计算难度。反之亦然
在线工具
缓动函数速查表 | http://www.xuanfengge.com/easeing/easeing/ | |
Ceaser | http://xuanfengge.com/easeing/ceaser/ | |
cubic-bezier | http://cubic-bezier.com/ | 在线调试 |
案例
计算控制点坐标分别为:P0(3,8),P1(2,3),P2(2,7),想要返回 10 个在贝塞尔曲线上的点
float[] p0 = {3, 8};
float[] p1 = {4, 3};
float[] p2 = {2, 7};
float[][] result = new float[10][2];
for (int i = 0; i < 10; i++) {
float t = i / 10;
result[i][0] = (float) (1 * Math.pow(1 - t, 2) * Math.pow(t, 0) * p0[0] + 2 * Math.pow(1 - t, 1) * Math.pow(t, 1) * p1[0] + 1 * Math.pow(1 - t, 0) * Math.pow(t, 2) * p2[0]);
result[i][1] = (float) (1 * Math.pow(1 - t, 2) * Math.pow(t, 0) * p0[1] + 2 * Math.pow(1 - t, 1) * Math.pow(t, 1) * p1[1] + 1 * Math.pow(1 - t, 0) * Math.pow(t, 2) * p2[1]);
计算贝塞尔曲线上所有点坐标
/**
* @param poss 贝塞尔曲线控制点坐标
* @param precision 需要计算的该条贝塞尔曲线上的点的数目
* @return 该条贝塞尔曲线上的点(二维坐标)
*/
public float[][] calculate(float[][] poss, int precision) {
//维度,坐标轴数(二维坐标,三维坐标...)
int dimersion = poss[0].length;
//贝塞尔曲线控制点数(阶数)
int number = poss.length;
//控制点数不小于 2 ,至少为二维坐标系
if (number < 2 || dimersion < 2)
return null;
float[][] result = new float[precision][dimersion];
//计算杨辉三角
int[] mi = new int[number];
mi[0] = mi[1] = 1;
for (int i = 3; i <= number; i++) {
int[] t = new int[i - 1];
for (int j = 0; j < t.length; j++) {
t[j] = mi[j];
}
mi[0] = mi[i - 1] = 1;
for (int j = 0; j < i - 2; j++) {
mi[j + 1] = t[j] + t[j + 1];
}
}
//计算坐标点
for (int i = 0; i < precision; i++) {
float t = (float) i / precision;
for (int j = 0; j < dimersion; j++) {
float temp = 0.0f;
for (int k = 0; k < number; k++) {
temp += Math.pow(1 - t, number - k - 1) * poss[k][j] * Math.pow(t, k) * mi[k];
}
result[i][j] = temp;
}
}
return result;
}
通过计算出来的点依次连接形成贝塞尔曲线
// calculate 方法在 BezierImpl 中实现
private BezierImpl bezier = new BezierImpl();
private Paint paint = new Paint();
float[][] poss = {
{353.0f, 383.0f},
{670.0f, 266.0f},
{403.0f, 128.0f},
{148.0f, 369.0f},
{400.0f, 513.0f},
{564.0f, 503.0f},
{582.0f, 378.0f},
{682.0f, 878.0f},
{182.0f, 878.0f}
};
@Override
protected void onDraw(Canvas canvas) {
float x0, y0, x, y;
paint.setColor(Color.DKGRAY);
paint.setStrokeWidth(3.0f);
x0 = poss[0][0];
y0 = poss[0][1];
for (int i = 1; i < poss.length; i++) {
x = poss[i][0];
y = poss[i][1];
//画控制点连线
canvas.drawLine(x0, y0, x, y, paint);
x0 = x;
y0 = y;
}
paint.setColor(Color.RED);
paint.setStrokeWidth(5.0f);
float[][] po = bezier.calculate(poss, 500);
x0 = po[0][0];
y0 = po[0][1];
for (int i = 1; i < 500; i++) {
x = po[i][0];
y = po[i][1];
//画贝塞尔曲线连线
canvas.drawLine(x0, y0, x, y, paint);
x0 = x;
y0 = y;
}
}