计算立方贝塞尔长度的便宜方法

对于立方贝塞尔长度的解析解似乎不存在,但这并不意味着不存在编码廉价解的编码。 便宜我的意思是在50-100 ns(或更少)的范围内。

有人知道这样的事吗? 也许有两类:

1)较少的错误,如1%但更慢的代码。 2)更多错误如20%但更快?

我通过谷歌扫描了一下,但它没有找到任何看起来像一个很好的解决方案。 只有像划分N个线段并将N sqrt相加的东西 – 太慢以获得更高的精度,并且对于2或3个段可能太不准确。

有更好的吗?

另一种选择是将弧长估计为弦和控制网之间的平均值。 在实践中:

Bezier bezier = Bezier (p0, p1, p2, p3); chord = (p3-p0).Length; cont_net = (p0 - p1).Length + (p2 - p1).Length + (p3 - p2).Length; app_arc_length = (cont_net + chord) / 2; 

然后,您可以递归地将样条线段分割为两个线段,并计算弧长直到收敛。 我测试了自己,它实际上收敛得非常快。 我从这个论坛得到了这个想法。

最简单的算法:使曲线变平并计算欧氏距离。 只要你想要一个近似的弧长,这个解决方案既快又便宜。 鉴于你的曲线的坐标LUT – 你在谈论速度,所以我假设你使用它们,并且不要经常重新计算坐标 – 这是一个简单的for循环与计数。 在通用代码中,使用dist函数计算两点之间的欧氏距离:

 var arclength = 0, last=LUT.length-1, i; for (i=0; i 

完成。 arclength现在是基于您在LUT中可以在曲线中形成的最大段数的近似弧长。 更大的潜在错误需要更快的东西? 控制段数。

 var arclength = 0, segCount = ..., last=LUT.length-2, step = last/segCount, s, i; for (s=0; s<=segCount; s++) { i = (s*step/last)|0; arclength += dist(LUT[i], LUT[i+1]); } 

这几乎是最简单的算法,它仍能生成甚至接近真实弧长的值。 更好的是,你将不得不使用更昂贵的数值方法(如勒让德 - 高斯求积法)。

如果你想知道原因,请点击“Bézier曲线上的A Primer” 的弧长部分 。

我为3点贝塞尔(下图)计算了长度的封闭forms表达式。 我没有试过制作4+以上的封闭表格。 这很难或很难表现和处理。 然而,通过使用弧长公式进行积分,诸如Runge-Kutta积分算法( 详见我的问答)这样的数值近似技术可以很好地工作。

下面是一些3点贝塞尔曲线的Java代码,包括点abc

  vx = 2*(bx - ax); vy = 2*(by - ay); wx = cx - 2*bx + ax; wy = cy - 2*by + ay; uu = 4*(wx*wx + wy*wy); if(uu < 0.00001) { return (float) Math.sqrt((cx - ax)*(cx - ax) + (cy - ay)*(cy - ay)); } vv = 4*(vx*wx + vy*wy); ww = vx*vx + vy*vy; 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))); 

首先你应该理解贝塞尔算法的使用,当我用c#编写一个程序时,我使用的是beziers,很多时候我不得不在bezier中找到一个点,这看起来似乎是第一眼看上去不牢固的。 所以我要做的就是在我的项目中的服装数学课上写立方贝塞尔函数。 所以我先与你分享代码。

  //--------------- My Costum Power Method ------------------\\ public static float FloatPowerX(float number, int power) { float temp = number; for (int i = 0; i < power - 1; i++) { temp *= number; } return temp; } //--------------- Bezier Drawer Code Bellow ------------------\\ public static void CubicBezierDrawer(Graphics graphics, Pen pen, float[] startPointPixel, float[] firstControlPointPixel , float[] secondControlPointPixel, float[] endPointPixel) { float[] px = new float[1111], py = new float[1111]; float[] x = new float[4] { startPointPixel[0], firstControlPointPixel[0], secondControlPointPixel[0], endPointPixel[0] }; float[] y = new float[4] { startPointPixel[1], firstControlPointPixel[1], secondControlPointPixel[1], endPointPixel[1] }; int i = 0; for (float t = 0; t <= 1F; t += 0.001F) { px[i] = FloatPowerX((1F - t), 3) * x[0] + 3 * t * FloatPowerX((1F - t), 2) * x[1] + 3 * FloatPowerX(t, 2) * (1F - t) * x[2] + FloatPowerX(t, 3) * x[3]; py[i] = FloatPowerX((1F - t), 3) * y[0] + 3 * t * FloatPowerX((1F - t), 2) * y[1] + 3 * FloatPowerX(t, 2) * (1F - t) * y[2] + FloatPowerX(t, 3) * y[3]; graphics.DrawLine(pen, px[i - 1], py[i - 1], px[i], py[i]); i++; } } 

如上所示,这是bezier函数的工作方式,它绘制与Microsoft Bezier函数相同的Bezier(我测试它)。 您可以通过增加数组大小和计数器大小或绘制elipse而不是line&...来使其更加准确。 所有这些都取决于您的需要和所需的准确度......

回到主要目标,问题是如何计算长度?

好的答案是我们有很多点,每个都有一个x coorinat和y坐标,它们记住了我们三角形的形状,特别是一个RightTriabgle形状。 因此,如果我们有点p1和p2,我们可以将它们的距离计算为RightTriangle Chord。 正如我们在学校的数学课中记得的那样,在类型RightTriangle的ABC三角形中,和弦长度是 - > Sqrt(Angle的FrontCostalLenght ^ 2 + Angle的SideCostalLeghth ^ 2);

在所有点之间存在这种关系,我们计算当前点和当前点之前的最后一点(exmp p [i - 1]&p [i])之间的长度,并将它们的总和存储在一个变量中。 让我们在下面的代码中显示它

 //--------------- My Costum Power Method ------------------\\ public static float FloatPower2(float number) { return number * number; } //--------------- My Bezier Lenght Calculator Method ------------------\\ public static float CubicBezierLenghtCalculator(float[] startPointPixel , float[] firstControlPointPixel, float[] secondControlPointPixel, float[] endPointPixel) { float[] tmp = new float[2]; float lenght = 0; float[] px = new float[1111], py = new float[1111]; float[] x = new float[4] { startPointPixel[0], firstControlPointPixel[0] , secondControlPointPixel[0], endPointPixel[0] }; float[] y = new float[4] { startPointPixel[1], firstControlPointPixel[1] , secondControlPointPixel[1], endPointPixel[1] }; int i = 0; for (float t = 0; t <= 1.0; t += 0.001F) { px[i] = FloatPowerX((1.0F - t), 3) * x[0] + 3 * t * FloatPowerX((1.0F - t), 2) * x[1] + 3F * FloatPowerX(t, 2) * (1.0F - t) * x[2] + FloatPowerX(t, 3) * x[3]; py[i] = FloatPowerX((1.0F - t), 3) * y[0] + 3 * t * FloatPowerX((1.0F - t), 2) * y[1] + 3F * FloatPowerX(t, 2) * (1.0F - t) * y[2] + FloatPowerX(t, 3) * y[3]; if (i > 0) { tmp[0] = Math.Abs(px[i - 1] - px[i]);// calculating costal lenght tmp[1] = Math.Abs(py[i - 1] - py[i]);// calculating costal lenght lenght += (float)Math.Sqrt(FloatPower2(tmp[0]) + FloatPower2(tmp[1]));// calculating the lenght of current RightTriangle Chord & add it each time to variable } i++; } return lenght; } 

如果你希望更快的计算,只需要减少px和py数组长度和loob计数。

我们还可以通过将px和py减少到数组长度来减少内存需求或者制作一个简单的双变量,但是因为条件情况Happend会增加我们的大OI并没有这样做。

希望它对你有所帮助。 如果还有其他问题请问。 最诚挚的问候,Heydar - 伊朗伊斯兰共和国。

    Interesting Posts