【android UI学习】贝塞尔曲线

mac2024-03-19  25

简介

Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线。 曲线定义:起始点、终止点、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化

一阶贝赛尔:

一阶

一阶贝塞尔曲线是一条直线,计算公式为: 

一阶计算公式

 

 二阶贝赛尔:

1.步骤一:在平面内选3个不同线的点并且依次用线段连接。如下所示..

 2.步骤二:在AB和BC线段上找出点D和点E,使得 AD/AB = BE/BC

 3.步骤三:连接DE,在DE上寻找点F,F点需要满足:DF/DE = AD/AB = BE/BC

 

4.步骤四:当D点从A到B,并且E点从B到C这条线路中所有的F点形成的线路,就是这条二阶贝塞尔曲线

 

计算公式如下 

二阶计算公式

 三阶贝塞尔:

1.步骤一:在平面内选4个不同线的点并且依次用线段连接。如下所示..

 

步骤二:分别找到E F G三个点,满足要求:AE/AB = BF/BC = CG/CD;在EH FG上找到H J点,满足EH/EF = FI/FG = HJ/HI;

最终达到AE/AB = BF/BC = CG/CD=EH/EF = FI/FG = HJ/HI

  步骤三:这个J点所形成的线,就是我们的三阶贝塞尔曲线:

计算公式如下:

 

三阶计算公式

 高阶贝塞尔曲线:

四阶贝塞尔曲线:

四阶

五阶贝塞尔曲线

五阶

 n阶贝赛尔曲线计算公式如下:

N阶计算公式

 贝塞尔曲线的代码实现

使用path自带的方法画

1:使用quadTo画二阶贝塞尔曲线

quadTo方法传入两个参数,第一个是二阶贝塞尔曲线的控制点,第二个是二阶贝塞尔曲线的结束点

//二阶贝塞尔曲线 mPath1.moveTo(100, 100);//起始点 mPath1.quadTo(400, 200, 10, 500);//二阶贝塞尔曲线 //绘制三个点 canvas.drawCircle(100, 100, 10, mPaint);//起始点 canvas.drawCircle(400, 200, 10, mPaint);//控制点 canvas.drawCircle(10, 500, 10, mPaint);//结束点 canvas.drawPath(mPath1, mPaint);

 绘制结果如下:

 2:使用cubicTo绘制三阶贝塞尔曲线

cubicTo方法传入参数分别为第一个控制点,第二个控制点,结束点

//三阶贝塞尔曲线 mPath1.moveTo(100, 100); mPath1.cubicTo(400, 200, 10, 500, 300, 700); canvas.drawCircle(100, 100, 10, mPaint);//起始点 canvas.drawCircle(400, 200, 10, mPaint);//控制点 canvas.drawCircle(120, 500, 10, mPaint);//控制点 canvas.drawCircle(300, 700, 10, mPaint);//结束点 canvas.drawPath(mPath1, mPaint);

绘制结果如下

 

使用递归方式画

  因为path只提供了二阶,三阶的绘制方法,所以,高阶贝塞尔曲线,就需要自己绘制了,我们这里使用递归来绘制

递归方法主要如下递归计算每个点的坐标值

/** * 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的值,false代表的是y的值 * @return */ private float deCastelJau(int i, int j, float t, boolean calculateX) { if (i == 1) { return calculateX ? (1 - t) * mControlPoints.get(j).x + t * mControlPoints.get(j + 1).x : (1 - t) * mControlPoints.get(j).y + t * mControlPoints.get(j + 1).y; } else { return (1 - t) * deCastelJau(i - 1, j, t, calculateX) + t * deCastelJau(i - 1, j + 1, t, calculateX); } }

 

完整方法如下

public class BezierView extends View { private Paint mPaint; private Paint mLinePointPaint; private Path mPath; private List<PointF> mControlPoints;//控制点集合(包含数据点) public BezierView(Context context) { super(context); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(4); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.RED); mLinePointPaint = new Paint(); mLinePointPaint.setAntiAlias(true); mLinePointPaint.setStrokeWidth(4); mLinePointPaint.setStyle(Paint.Style.STROKE); mLinePointPaint.setColor(Color.GRAY); mPath = new Path(); mControlPoints = new ArrayList<>(); init(); } private void init() { mControlPoints.clear(); //添加起始点 PointF pointF = new PointF(200,200); //添加控制点 PointF pointF1 = new PointF(1000,550); PointF pointF2 = new PointF(600,1150); //结束点 PointF pointF3 = new PointF(1200,1350); mControlPoints.add(pointF); mControlPoints.add(pointF1); mControlPoints.add(pointF2); mControlPoints.add(pointF3); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //控制点及控制点的连线 int size = mControlPoints.size(); PointF pointF; for (int i = 0; i < size; i++) { pointF = mControlPoints.get(i); canvas.drawCircle(pointF.x, pointF.y, 10, mLinePointPaint); } //画曲线 buildBezierPoints(); canvas.drawPath(mPath, mPaint); } /** * 划线 */ private void buildBezierPoints() { mPath.reset(); int order = mControlPoints.size() - 1;//阶数 //份数 float delta = 1.0f / 1000; //将一条线分为了1000个点来显示,这里的数量可以自己控制,越多越精确 for (float t = 0; t <= 1; t += delta) { //bezier点集 PointF pointF = new PointF(deCastelJau(order, 0, t, true), deCastelJau(order, 0, t, false));//计算在曲线上点位置 if (t ==0) { mPath.moveTo(pointF.x, pointF.y); } else { mPath.lineTo(pointF.x, pointF.y); } } } /** * 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的值,false代表的是y的值 * @return */ private float deCastelJau(int i, int j, float t, boolean calculateX) { if (i == 1) { return calculateX ? (1 - t) * mControlPoints.get(j).x + t * mControlPoints.get(j + 1).x : (1 - t) * mControlPoints.get(j).y + t * mControlPoints.get(j + 1).y; } else { return (1 - t) * deCastelJau(i - 1, j, t, calculateX) + t * deCastelJau(i - 1, j + 1, t, calculateX); } } }

我们这里只添加了四个点 所以上面的代码运行结果如下:

 

最新回复(0)