我有2d点数组,我需要创建一个通过所有点的Path
。我想我应该使用Path.cubicTo()
方法,使用指定的控制点在两点之间创建贝塞尔曲线。问题是我不知道曲线的控制点。我该如何计算它们?
也许有更好的方法吗?也许有某种类型的库可以帮助我吗?
答案 0 :(得分:2)
在我阅读this篇文章后,它变得非常简单。
这是你在android上的方法。运行此代码后,您的路径p
将遍历knotsArr
数组中的所有点。
Point[] knotsArr = {new Point(0, 0),
new Point(5, 5),
new Point(10, 0),
new Point(15, 5)};
Point[][] controlPoints = BezierSplineUtil.getCurveControlPoints(knotsArr);
Point[] firstCP = controlPoints[0];
Point[] secondCP = controlPoints[1];
Path p = new Path();
p.moveTo(knots.get(0).x, knots.get(0).y);
for (int i = 0; i < firstCP.length; i++) {
p.cubicTo(firstCP[i].x, firstCP[i].y,
secondCP[i].x, secondCP[i].y,
knots.get(i + 1).x, knots.get(i + 1).y);
}
BezierSplineUtil.java
public class BezierSplineUtil {
public static class Point {
public final float x;
public final float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
}
/**
* Get open-ended bezier spline control points.
*
* @param knots bezier spline points.
* @return [2 x knots.length - 1] matrix. First row of the matrix = first
* control points. Second row of the matrix = second control points.
* @throws IllegalArgumentException if less than two knots are passed.
*/
public static Point[][] getCurveControlPoints(Point[] knots) {
if (knots == null || knots.length < 2) {
throw new IllegalArgumentException("At least two knot points are required");
}
final int n = knots.length - 1;
final Point[] firstControlPoints = new Point[n];
final Point[] secondControlPoints = new Point[n];
// Special case: bezier curve should be a straight line
if (n == 1) {
// 3P1 = 2P0 + P3
float x = (2 * knots[0].x + knots[1].x) / 3;
float y = (2 * knots[0].y + knots[1].y) / 3;
firstControlPoints[0] = new Point(x, y);
// P2 = 2P1 - P0
x = 2 * firstControlPoints[0].x - knots[0].x;
y = 2 * firstControlPoints[0].y - knots[0].y;
secondControlPoints[0] = new Point(x, y);
return new Point[][] { firstControlPoints, secondControlPoints };
}
// Calculate first bezier control points
// Right hand side vector
float[] rhs = new float[n];
// Set right hand side X values
for (int i = 1; i < n - 1; i++) {
rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
}
rhs[0] = knots[0].x + 2 * knots[1].x;
rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2f;
// Get first control points X-values
float[] x = getFirstControlPoints(rhs);
// Set right hand side Y values
for (int i = 1; i < n - 1; i++) {
rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
}
rhs[0] = knots[0].y + 2 * knots[1].y;
rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2f;
// Get first control points Y-values
float[] y = getFirstControlPoints(rhs);
for (int i = 0; i < n; i++) {
// First control point
firstControlPoints[i] = new Point(x[i], y[i]);
// Second control point
if (i < n - 1) {
float xx = 2 * knots[i + 1].x - x[i + 1];
float yy = 2 * knots[i + 1].y - y[i + 1];
secondControlPoints[i] = new Point(xx, yy);
} else {
float xx = (knots[n].x + x[n - 1]) / 2;
float yy = (knots[n].y + y[n - 1]) / 2;
secondControlPoints[i] = new Point(xx, yy);
}
}
return new Point[][] { firstControlPoints, secondControlPoints };
}
/**
* Solves a tridiagonal system for one of coordinates (x or y) of first
* bezier control points.
*
* @param rhs right hand side vector.
* @return Solution vector.
*/
private static float[] getFirstControlPoints(float[] rhs) {
int n = rhs.length;
float[] x = new float[n]; // Solution vector
float[] tmp = new float[n]; // Temp workspace
float b = 2.0f;
x[0] = rhs[0] / b;
// Decomposition and forward substitution
for (int i = 1; i < n; i++) {
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0f : 3.5f) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
// Backsubstitution
for (int i = 1; i < n; i++) {
x[n - i - 1] -= tmp[n - i] * x[n - i];
}
return x;
}
}