我试图找到一种算法来计算给定的三次贝塞尔曲线的边界框。曲线在3D空间中。
除了曲线上的采样点和计算这些点的边界框之外,是否有一种数学方法可以做到这一点?
答案 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)}
};
}