另请参阅: 关于Math.SE的相同问题
如何找到贝塞尔曲线的弧长?例如,线性贝塞尔曲线的长度为:
length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));
但是,二次,三次或n度贝塞尔曲线呢?
(我的目标是事先估计采样分辨率,因此,我不必浪费时间检查下一个点是否在触摸上一个点。)
另请参阅: 关于Math.SE的相同问题
如何找到贝塞尔曲线的弧长?例如,线性贝塞尔曲线的长度为:
length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));
但是,二次,三次或n度贝塞尔曲线呢?
(我的目标是事先估计采样分辨率,因此,我不必浪费时间检查下一个点是否在触摸上一个点。)
Answers:
三次方贝塞尔曲线的一种简单方法是将曲线分为N段,然后将段的长度相加。
但是,只要您只需要曲线的一部分的长度(例如,达到沿其长度的30%的一点),弧长参数设置就会起作用。我用一个简单的示例代码就我对贝塞尔的一个问题发表了相当长的答案。
1.0/t
(称为resolution
)的值,以实现“实时”(在慢速NXT上最高为10fps)。每次迭代t += resolution
都会绘制一条新的点/线。无论如何,谢谢你的想法。
尽管我已经回答了您已经得到的答案,但我想添加一个简单但功能强大的逼近机制,该机制可用于任何Bézier度曲线:您可以使用de Casteljau细分不断细分曲线,直到控制点的最大距离子曲线相对于子曲线基线的坐标低于某个恒定的ε。在那种情况下,子曲线可以通过其基线来近似。
实际上,我相信这是图形子系统必须绘制贝塞尔曲线时通常采用的方法。但是,请不要在此引用我的信息,目前我没有参考资料。
实际上,它看起来像这样:(除非语言无关)
public static Line[] toLineStrip(BezierCurve bezierCurve, double epsilon) {
ArrayList<Line> lines = new ArrayList<Line>();
Stack<BezierCurve> parts = new Stack<BezierCurve>();
parts.push(bezierCurve);
while (!parts.isEmpty()) {
BezierCurve curve = parts.pop();
if (distanceToBaseline(curve) < epsilon) {
lines.add(new Line(curve.get(0), curve.get(1)));
} else {
parts.addAll(curve.split(0.5));
}
}
return lines.toArray(new Line[0]);
}
贝塞尔曲线的弧长仅对于线性和二次曲线是封闭形式。对于立方,不能保证有封闭的解决方案。原因是弧长由基本积分定义,对于该积分只有二阶多项式是闭合的。
仅供参考:(a,p)(b,q)和(c,r)点的二次Bezier的长度为
(a ^ 2·(q ^ 2-2·q·r + r ^ 2)+ 2·a·(r-q)·(b·(p-r)+ c·(q-p))+( b·(p-r)+ c·(q-p))^ 2)·LN((√(a ^ 2-2·a·b + b ^ 2 + p ^ 2-2·p·q + q ^ 2)·√(a ^ 2 + 2·a·(c-2·b)+ 4·b ^ 2-4-b·c + c ^ 2 +(p -2·q + r)^ 2) + a ^ 2 + a·(c-3·b)+ 2·b ^ 2-b·c +(p-q)·(p-2·q + r))/(√(a ^ 2 + 2 ·a·(c-2·b)+ 4·b ^ 2-4·b·c + c ^ 2 +(p -2·q + r)^ 2)·√(b ^ 2-2·b· c + c ^ 2 + q ^ 2-2·q·r + r ^ 2)+ a·(b-c)-2·b ^ 2 + 3·b·c-c ^ 2 +(p -2· q + r)·(q-r)))/(a ^ 2 + 2·a·(c -2·b)+ 4·b ^ 2-4-4·b·c + c ^ 2 +(p-2 ·q + r)^ 2)^(3/2)+(√(a ^ 2-2·a·b + b ^ 2 + p ^ 2-2·p·q + q ^ 2)·(a ^ 2 + a·(c-3·b)+ 2·b ^ 2-b·c +(p-q)·(p-2·q + r))-√(b ^ 2-2·b·c + c ^ 2 + q ^ 2-2·q·r + r ^ 2)·(a·(b-c)-2·b ^ 2 + 3·b·c-c ^ 2 +(p -2· q + r)·(q-r)))/(a ^ 2 + 2·a·(c -2·b)+ 4·b ^ 2-4-4·b·c + c ^ 2 +(p-2 ·q + r)^ 2)
其中LN是自然对数,^表示幂,√表示平方根。
因此,应该通过其他规则(例如多边形或像Simpson规则那样的积分方案)来更容易和更便宜地近似圆弧,因为LN的平方根是昂贵的运算。
我计算出一个3点Bezier(如下)的长度的封闭形式表达式。我没有尝试为4分以上的分数制定封闭表格。这很可能难以表达或处理。但是,通过使用弧长公式进行积分,诸如Runge-Kutta积分算法之类的数值逼近技术会很好地工作。我对MSE 上RK45的问答可能对RK45实施有所帮助。
下面是一个3点贝塞尔的弧长一些Java代码,以点a
,b
和c
。
v.x = 2*(b.x - a.x);
v.y = 2*(b.y - a.y);
w.x = c.x - 2*b.x + a.x;
w.y = c.y - 2*b.y + a.y;
uu = 4*(w.x*w.x + w.y*w.y);
if(uu < 0.00001)
{
return (float) Math.sqrt((c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y));
}
vv = 4*(v.x*w.x + v.y*w.y);
ww = v.x*v.x + v.y*v.y;
t1 = (float) (2*Math.sqrt(uu*(uu + vv + ww)));
t2 = 2*uu+vv;
t3 = vv*vv - 4*uu*ww;
t4 = (float) (2*Math.sqrt(uu*ww));
return (float) ((t1*t2 - t3*Math.log(t2+t1) -(vv*t4 - t3*Math.log(vv+t4))) / (8*Math.pow(uu, 1.5)));