立方贝塞尔长度的解析解 似乎不存在,但这并不意味着 编码廉价的解决方案不存在。便宜我的意思是在50-100 ns(或更少)的范围内。
有人知道这样的事吗?可能分为两类:
1)较少的错误,如1%但代码更慢。 2)更多错误如20%但更快?
我通过谷歌扫描了一下,但事实并非如此 找到任何看起来不错的解决方案。只有在N个线段上划分的东西 求和N sqrt - 太慢以获得更高的精度, 并且可能对2或3段不准确。
有什么更好的吗?
答案 0 :(得分:15)
另一种选择是将弧长估计为和弦与控制网之间的平均值。在实践中:
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;
然后,您可以递归地将样条线段分割为两个线段,并计算弧长到收敛。我测试了自己,它实际上收敛得非常快。我从这个forum得到了这个想法。
答案 1 :(得分:4)
最简单的算法:展平曲线并计算欧氏距离。只要你想要一个近似的弧长,这个解决方案既快又便宜。鉴于你的曲线的坐标LUT - 你在谈论速度,所以我假设你使用它们,并且不要经常重新计算坐标 - 这是一个简单的for循环与计数。在通用代码中,使用dist
函数计算两点之间的欧氏距离:
var arclength = 0,
last=LUT.length-1,
i;
for (i=0; i<last; i++) {
arclength += dist(LUT[i], LUT[i+1]);
}
完成。 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曲线的入门”the arc length section。
答案 2 :(得分:0)
我为3点贝塞尔(下图)计算了长度的封闭形式表达式。我没有试图计算4+以上的封闭表格。这很难或很难表现和处理。然而,通过使用see my Q&A here for details进行积分,诸如Runge-Kutta积分算法(arc length formula)之类的数值近似技术可以很好地工作。
以下是3点Bezier弧长的一些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)));
答案 3 :(得分:0)
public float FastArcLength()
{
float arcLength = 0.0f;
ArcLengthUtil(cp0.position, cp1.position, cp2.position, cp3.position, 5, ref arcLength);
return arcLength;
}
private void ArcLengthUtil(Vector3 A, Vector3 B, Vector3 C, Vector3 D, uint subdiv, ref float L)
{
if (subdiv > 0)
{
Vector3 a = A + (B - A) * 0.5f;
Vector3 b = B + (C - B) * 0.5f;
Vector3 c = C + (D - C) * 0.5f;
Vector3 d = a + (b - a) * 0.5f;
Vector3 e = b + (c - b) * 0.5f;
Vector3 f = d + (e - d) * 0.5f;
// left branch
ArcLengthUtil(A, a, d, f, subdiv - 1, ref L);
// right branch
ArcLengthUtil(f, e, c, D, subdiv - 1, ref L);
}
else
{
float controlNetLength = (B-A).magnitude + (C - B).magnitude + (D - C).magnitude;
float chordLength = (D - A).magnitude;
L += (chordLength + controlNetLength) / 2.0f;
}
}
答案 4 :(得分:0)
public static float BezierSingleLength(Vector3[] points){
var p0 = points[0] - points[1];
var p1 = points[2] - points[1];
var p2 = new Vector3();
var p3 = points[3]-points[2];
var l0 = p0.magnitude;
var l1 = p1.magnitude;
var l3 = p3.magnitude;
if(l0 > 0) p0 /= l0;
if(l1 > 0) p1 /= l1;
if(l3 > 0) p3 /= l3;
p2 = -p1;
var a = Mathf.Abs(Vector3.Dot(p0,p1)) + Mathf.Abs(Vector3.Dot(p2,p3));
if(a > 1.98f || l0 + l1 + l3 < (4 - a)*8) return l0+l1+l3;
var bl = new Vector3[4];
var br = new Vector3[4];
bl[0] = points[0];
bl[1] = (points[0]+points[1]) * 0.5f;
var mid = (points[1]+points[2]) * 0.5f;
bl[2] = (bl[1]+mid) * 0.5f;
br[3] = points[3];
br[2] = (points[2]+points[3]) * 0.5f;
br[1] = (br[2]+mid) * 0.5f;
br[0] = (br[1]+bl[2]) * 0.5f;
bl[3] = br[0];
return BezierSingleLength(bl) + BezierSingleLength(br);
}
答案 5 :(得分:-1)
首先你要了解Bezier中的算法用法, 当我用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&amp;来使其更加准确。 ......所有这些都取决于您的需要和所需的准确度......
回到主要目标,问题是如何计算长度???
答案是答案是我们有很多点,每个都有一个x coorinat和y坐标,它们记住了我们一个三角形的形状。特别是一个RightTriabgle形状。所以,如果我们有点p1&amp; p2,我们可以将它们的距离计算为RightTriangle Chord。正如我们在学校的数学课上记得的那样,在类型RightTriangle的ABC三角形中,chord Lenght是 - &gt; Sqrt(Angle的FrontCostalLenght ^ 2 + Angle的SideCostalLeghth ^ 2);并且在所有点之间存在这种关系,我们计算当前点和当前点之前的最后一点(exmp p [i-1]&amp; 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&amp; py array lenght and loob count。
我们也可以通过将px和py减少到数组长度来减少内存需求或者制作一个简单的双变量,但是因为条件情况Happend会增加我们的大O我没有这样做。
希望它对你有所帮助。如果还有其他问题请问。 最诚挚的问候,Heydar - 伊朗伊斯兰共和国。