貝塞爾曲線工具類
先上一張效果圖看效果
貝塞爾曲線
用於計算N階貝塞爾曲線上的點,根據傳入控制點的個數判定階數N
貝塞爾曲線計算公式:
public class BezierUtils {
/**
* 獲取二項式係數
*
* @param l 行(楊輝三角)
* @param c 列(楊輝三角)
* @return 係數
*/
private static int getBinomialCoefficient(@IntRange(from = 0) int l, @IntRange(from = 0) int c) {
int lotteryOdds = 1;
for (int i = 1; i <= c; i++)
lotteryOdds = lotteryOdds * (l - i + 1) / i;
return lotteryOdds;
}
/**
* 獲取貝塞爾曲線上t位置的點
*
* @param t 變化值
* @param points 控制點
* @return 當前t對應的點
*/
public static PointF getBezierPoint(@FloatRange(from = 0, to = 1) float t, List<PointF> points) {
if (points == null || points.size() < 2)
return null;
PointF result = new PointF();
int n = points.size() - 1;//N階
for (int i = 0; i <= n; i++) {
int binomialCoefficient = getBinomialCoefficient(n, i);
double v = Math.pow(1 - t, n - i) * Math.pow(t, i);
result.x += binomialCoefficient * (points.get(i).x * v);
result.y += binomialCoefficient * (points.get(i).y * v);
}
return result;
}
/**
* 獲取貝塞爾曲線上t位置的點
*
* @param t 變化值
* @param points 控制點
* @return 當前t對應的點
*/
public static PointF getBezierPoint(@FloatRange(from = 0, to = 1) float t, PointF... points) {
if (points == null || points.length < 2)
return null;
return getBezierPoint(t, Arrays.asList(points));
}
/**
* 獲取貝塞爾曲線路徑
*
* @param precision 生成路徑分段的精度
* @param points 控制點
* @return 貝塞爾曲線路徑
*/
public static Path getBezierPath(@IntRange(from = 1) int precision, List<PointF> points) {
if (precision < 1 || points == null || points.size() < 2)
return null;
float division = 1f / precision;
float t = division;
PointF lastP = points.get(0);
Path path = new Path();
path.moveTo(lastP.x, lastP.y);
while (t < 1) {
PointF point = getBezierPoint(t, points);
path.lineTo(point.x, point.y);
t += division;
}
PointF endP = points.get(points.size() - 1);
path.lineTo(endP.x, endP.y);
return path;
}
/**
* 獲取貝塞爾曲線路徑
*
* @param precision 生成路徑分段的精度
* @param points 控制點
* @return 貝塞爾曲線路徑
*/
public static Path getBezierPath(@IntRange(from = 1) int precision, PointF... points) {
if (precision < 1 || points == null || points.length < 2)
return null;
return getBezierPath(precision, Arrays.asList(points));
}
}
示例代碼
public class MyView extends View {
private PointF f1;
private PointF f2;
private PointF f3;
private PointF f4;
private Paint paint;
private Paint paint2;
private Paint paint3;
private Path path1;
private Path path2;
private Path path3;
private PointF point;
private PointF point1;
private PointF point2;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
paint.setStrokeWidth(20);
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setDither(true);
paint2 = new Paint();
paint2.setStrokeWidth(10);
paint2.setColor(Color.BLUE);
paint2.setStyle(Paint.Style.STROKE);
paint2.setAntiAlias(true);
paint2.setDither(true);
paint3 = new Paint();
paint3.setStrokeWidth(15);
paint3.setColor(Color.YELLOW);
paint3.setAntiAlias(true);
paint3.setDither(true);
path1 = new Path();
path2 = new Path();
path3 = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int measuredHeight = getMeasuredHeight();
int measuredWidth = getMeasuredWidth();
f1 = new PointF(measuredWidth / 3, measuredHeight / 3);
f2 = new PointF(measuredWidth / 3, measuredHeight / 3 * 2);
f3 = new PointF(measuredWidth / 3 * 2, measuredHeight / 3);
f4 = new PointF(measuredWidth / 3 * 2, measuredHeight / 3 * 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫四個控制點(圖中黑色的四個點)
canvas.drawPoint(f1.x, f1.y, paint);
canvas.drawPoint(f2.x, f2.y, paint);
canvas.drawPoint(f3.x, f3.y, paint);
canvas.drawPoint(f4.x, f4.y, paint);
//系統API畫3階貝塞爾曲線(圖中藍色線條)
path1.moveTo(f1.x, f1.y);
path1.cubicTo(f1.x, f1.y, f2.x, f2.y, f3.x, f3.y);
paint2.setColor(Color.BLUE);
canvas.drawPath(path1, paint2);
//系統API畫3階貝塞爾曲線(圖中綠色線條)
path2.moveTo(f1.x, f1.y);
path2.cubicTo(f2.x, f2.y, f3.x, f3.y, f4.x, f4.y);
paint2.setColor(Color.GREEN);
canvas.drawPath(path2, paint2);
//系統API畫2階貝塞爾曲線(圖中灰色線條)
path3.moveTo(f1.x, f1.y);
paint2.setColor(Color.LTGRAY);
path3.quadTo(f2.x, f2.y, f4.x, f4.y);
canvas.drawPath(path3, paint2);
//畫圖中三個黃色變動的點(取自灰色線條)
if (point != null) {
canvas.drawPoint(point.x, point.y, paint3);
}
//畫圖中三個黃色變動的點(取自藍色線條)
if (point1 != null) {
canvas.drawPoint(point1.x, point1.y, paint3);
}
//畫圖中三個黃色變動的點(取自綠色線條)
if (point2 != null) {
canvas.drawPoint(point2.x, point2.y, paint3);
}
}
//畫圖中三個黃色變動的點(取自灰色線條)
public void drawPoint(@FloatRange(from = 0, to = 1) float t) {
point = BezierUtils.getBezierPoint(t, f1, f2, f4);
invalidate();
}
//畫圖中三個黃色變動的點(取自綠色線條)
public void drawPoint2(@FloatRange(from = 0, to = 1) float t) {
point2 = BezierUtils.getBezierPoint(t, f1, f2, f3, f4);
invalidate();
}
//畫圖中三個黃色變動的點(取自藍色線條)
public void drawPoint1(@FloatRange(from = 0, to = 1) float t) {
point1 = BezierUtils.getBezierPoint(t, f1, f1, f2, f3);
invalidate();
}
}