计算三次贝塞尔曲线的边界框

时间:2014-07-17 17:33:04

标签: math 3d curve bezier

我试图找到一种算法来计算给定的三次贝塞尔曲线的边界框。曲线在3D空间中。

除了曲线上的采样点和计算这些点的边界框之外,是否有一种数学方法可以做到这一点?

3 个答案:

答案 0 :(得分:6)

大多数情况都在An algorithm to find bounding box of closed bezier curves?中解决,除了这里我们有三次贝塞尔曲线并且它们处理的是二次贝塞尔曲线。

基本上你需要采用每个坐标函数的导数。如果x-coord由

给出
x = A (1-t)^3 +3 B t (1-t)^2 + 3 C t^2 (1-t) + D t^3

区别于t。

dx/dt =  3 (B - A) (1-t)^2 + 6 (C - B) (1-t) t + 3 (D - C) t^2
      =  [3 (D - C) - 6 (C - B) + 3 (B - A)] t^2
       + [ -6 (B - A) - 6 (C - B)] t
       + 3 (B - A) 
      =  (3 D - 9 C + 9 B - 3 A) t^2 + (6 A - 12 B + 6 C) t + 3 (B - A)

这是我们可以在a t^2 + b t + c写的二次方。我们想要使用二次公式

来解决dx/dt = 0
- b +/- sqrt(b^2-4 a c)
-----------------------
        2 a

解决这个问题要么提供两个解决方案t0,t1说没有解决方案,或者在极少数情况下只有一个解决方案。我们只对0 <= t <= 1的解决方案感兴趣。您将有最多四个候选点,两个端点和两个解决方案。找到其中哪一个给出了极端的观点是一件简单的事情。

您可以为每个坐标重复相同的过程,然后获取边界框。

我把它放在js小提琴http://jsfiddle.net/SalixAlba/QQnvm/4/

中的二维个案中

答案 1 :(得分:6)

通常通过查找曲线控制多边形的边界框来查找三次贝塞尔曲线(或任何度数的B样条曲线)的边界框。由于曲线始终由其控制多边形限定,因此通过控制多边形获得的边界框保证包围曲线。您还可以对曲线执行结点插入,并使控制多边形变得更接近曲线本身。因此,通用算法将如下所示:

1)从控制多边形中找到当前B样条曲线的边界框(表示为BBox1)。
2)在B样条曲线中的每个贝塞尔曲线段的中间参数处插入结 3)找到新B样条曲线的边界框(表示为BBox2) 4)将BBox2与BBox1进行比较。如果BBox2与BBox1几乎相同,我们就完成了。如果BBox2比BBox1小得多,重复步骤2到4直到收敛。

答案 2 :(得分:0)

这篇文章解释了细节,还有一个实时的html5演示:
Calculating / Computing the Bounding Box of Cubic Bezier

我在Snap.svg中找到了一个javascript来计算:here
请参阅bezierBBox和curveDim函数。

我重写了一个javascript函数。

//(x0,y0) is start point; (x1,y1),(x2,y2) is control points; (x3,y3) is end point.
function bezierMinMax(x0, y0, x1, y1, x2, y2, x3, y3) {
    var tvalues = [], xvalues = [], yvalues = [],
        a, b, c, t, t1, t2, b2ac, sqrtb2ac;
    for (var i = 0; i < 2; ++i) {
        if (i == 0) {
            b = 6 * x0 - 12 * x1 + 6 * x2;
            a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
            c = 3 * x1 - 3 * x0;
        } else {
            b = 6 * y0 - 12 * y1 + 6 * y2;
            a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
            c = 3 * y1 - 3 * y0;
        }
        if (Math.abs(a) < 1e-12) {
            if (Math.abs(b) < 1e-12) {
                continue;
            }
            t = -c / b;
            if (0 < t && t < 1) {
                tvalues.push(t);
            }
            continue;
        }
        b2ac = b * b - 4 * c * a;
        if (b2ac < 0) {
            if (Math.abs(b2ac) < 1e-12) {
                t = -b / (2 * a);
                if (0 < t && t < 1) {
                    tvalues.push(t);
                }
            }
            continue;
        }
        sqrtb2ac = Math.sqrt(b2ac);
        t1 = (-b + sqrtb2ac) / (2 * a);
        if (0 < t1 && t1 < 1) {
            tvalues.push(t1);
        }
        t2 = (-b - sqrtb2ac) / (2 * a);
        if (0 < t2 && t2 < 1) {
            tvalues.push(t2);
        }
    }

    var j = tvalues.length, mt;
    while (j--) {
        t = tvalues[j];
        mt = 1 - t;
        xvalues[j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
        yvalues[j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
    }

    xvalues.push(x0,x3);
    yvalues.push(y0,y3);

    return {
        min: {x: Math.min.apply(0, xvalues), y: Math.min.apply(0, yvalues)},
        max: {x: Math.max.apply(0, xvalues), y: Math.max.apply(0, yvalues)}
    };
}