贝塞尔曲线弧长


23

另请参阅: 关于Math.SE的相同问题

如何找到贝塞尔曲线的弧长?例如,线性贝塞尔曲线的长度为:

length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));

但是,二次,三次或n度贝塞尔曲线呢?

(我的目标是事先估计采样分辨率,因此,我不必浪费时间检查下一个点是否在触摸上一个点。)


1
您应该对问题重新措词,以指代曲线的长度,这是一个更简单(且可搜索)的术语。
Sparr

我建议将其发布在数学上,我敢肯定那里有一些聪明的面孔会为您提供其中一种聪明的网络字体的答案:p
Tor Valamo 2010年

2
@Tor我昨天(昨天)做了,但是有人告诉我它非常复杂,因此不切实际。[ math.stackexchange.com/q/12186/2736 ]
Mateen Ulhaq,2010年

假设回旋曲线/样条曲线是贝塞尔曲线的替代方法,并且具有闭合形式的弧长表达式,但是我对此还不了解。(尝试沿曲线生成等距点。)悬链线也有闭合形式的弧长表达式?
endlith 2014年

Answers:


9

三次方贝塞尔曲线的一种简单方法是将曲线分为N段,然后将段的长度相加。

但是,只要您只需要曲线的一部分的长度(例如,达到沿其长度的30%的一点),弧长参数设置就会起作用。我用一个简单的示例代码就我对贝塞尔的一个问题发表了相当长的答案


我正在为乐高Mindstorms NXT这样做,它的处理器(48Mhz)确实很弱,所以我需要尽可能多的速度。我将采用除法方法来保存一些速度,并使其足够准确(用于“非实时”渲染)。我还提供了一个选项,您可以在其中设置1.0/t(称为resolution)的值,以实现“实时”(在慢速NXT上最高为10fps)。每次迭代t += resolution都会绘制一条新的点/线。无论如何,谢谢你的想法。
Mateen Ulhaq 2010年

4

尽管我已经回答了您已经得到的答案,但我想添加一个简单但功能强大的逼近机制,该机制可用于任何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]);
}

尽管这是一个好方法,但我听说过高阶贝塞尔曲线的数值不稳定,这需要另一个想法:将高阶曲线拆分为较小的三次曲线。
Mateen Ulhaq 2015年

另外,如果最终目标是准确的估算,最好用二次方而不是直线进行近似,以确保在高曲率的位置上不要低估估算值。
Mateen Ulhaq 2015年

2

贝塞尔曲线的弧长仅对于线性和二次曲线是封闭形式。对于立方,不能保证有封闭的解决方案。原因是弧长由基本积分定义,对于该积分只有二阶多项式是闭合的。

仅供参考:(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的平方根是昂贵的运算。


2

我计算出一个3点Bezier(如下)的长度的封闭形式表达式。我没有尝试为4分以上的分数制定封闭表格。这很可能难以表达或处理。但是,通过使用弧长公式进行积分,诸如Runge-Kutta积分算法之类的数值逼近技术会很好地工作。我对MSE 上RK45的问答可能对RK45实施有所帮助。

下面是一个3点贝塞尔的弧长一些Java代码,以点abc

    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)));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.