Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 三次贝塞尔长度的廉价计算方法_C_Algorithm_Graphics_Bezier - Fatal编程技术网

C 三次贝塞尔长度的廉价计算方法

C 三次贝塞尔长度的廉价计算方法,c,algorithm,graphics,bezier,C,Algorithm,Graphics,Bezier,三次bezier长度的解析解 似乎不存在,但这并不意味着 编写廉价的解决方案是不存在的。我所说的便宜是指在50-100纳秒(或更少)范围内的东西 有人知道这样的事吗?可能分为两类: 1) 错误更少,如1%,但代码更慢。 2) 更多错误,比如20%,但更快 我浏览了一下谷歌,但没有 找到任何看起来不错的解决方案。只有类似于N条线段上的除法 求N sqrt的和-太慢,无法获得更高的精度, 对于2或3段来说可能太不准确了 还有更好的吗 最简单的算法:展平曲线并计算欧氏距离。只要你想要一个近似的弧长,这

三次bezier长度的解析解 似乎不存在,但这并不意味着 编写廉价的解决方案是不存在的。我所说的便宜是指在50-100纳秒(或更少)范围内的东西

有人知道这样的事吗?可能分为两类:

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

我浏览了一下谷歌,但没有 找到任何看起来不错的解决方案。只有类似于N条线段上的除法 求N sqrt的和-太慢,无法获得更高的精度, 对于2或3段来说可能太不准确了


还有更好的吗

最简单的算法:展平曲线并计算欧氏距离。只要你想要一个近似的弧长,这个解决方案是快速和便宜的。给定你的曲线坐标LUT,你说的是速度,所以我假设你使用这些,不要不断地重新计算坐标,这是一个简单的带计数的for循环。在通用代码中,使用计算两点之间欧几里德距离的
dist
函数:

var arclength = 0,
    last=LUT.length-1,
    i;
for (i=0; i<last; i++) {
  arclength += dist(LUT[i], LUT[i+1]);
}
这几乎是最简单的算法,仍然可以生成更接近真实弧长的值。为了更好,你必须使用更昂贵的数值方法(比如勒让德-高斯求积技术)


如果您想知道原因,请点击“Bézier曲线上的底漆”。

另一个选项是将弧长估计为弦和控制网之间的平均值。实际上:

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;

然后可以递归地将样条曲线分段为两段,并计算弧长直至收敛。我测试了自己,它实际上收敛得相当快。我从中得到了这个想法。

首先,你应该了解贝塞尔算法的使用, 当我用c#编写一个充满图形材料的程序时,我使用了bezier,很多时候我不得不在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++;
        }
    }
/--------------我的肋骨力量法------------------\\
公共静态浮点浮点POWERX(浮点数,整数幂)
{
浮动温度=数字;
对于(int i=0;i对于(float t=0;t我为3点Bezier(如下)计算出了长度的闭合形式表达式。我没有尝试为4+点计算出闭合形式。这很可能很难或很复杂地表示和处理。但是,数值近似技术,如龙格-库塔积分算法()通过使用

下面是一些关于三点贝塞尔曲线弧长的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)));
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);
如果(uu<0.00001)
{
返回(浮点)数学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=(浮点)(2*数学sqrt(uu*(uu+vv+ww));
t2=2*uu+vv;
t3=vv*vv-4*uu*ww;
t4=(浮动)(2*数学sqrt(uu*ww));
返回(浮动)(t1*t2-t3*数学日志(t2+t1)-(vv*t4-t3*数学日志(vv+t4))/(8*数学功率(uu,1.5));

在我的例子中,一种快速有效的方法是这样的(用c#为Unity3d重写)

公共静态浮点BezierSingleLength(矢量3[]点){
var p0=点[0]-点[1];
变量p1=点[2]-点[1];
var p2=新向量3();
var p3=点[3]-点[2];
var l0=p0.01量级;
var l1=p1.5量级;
var l3=p3.0级;
如果(l0>0)p0/=l0;
如果(l1>0)p1/=l1;
如果(l3>0)p3/=l3;
p2=-p1;
变量a=Mathf.Abs(向量3.Dot(p0,p1))+Mathf.Abs(向量3.Dot(p2,p3));
如果(a>1.98f | | l0+l1+l3<(4-a)*8)返回l0+l1+l3;
var bl=新向量3[4];
var br=新向量3[4];
bl[0]=点[0];
bl[1]=(点数[0]+点数[1])*0.5f;
var mid=(点[1]+点[2])*0.5f;
bl[2]=(bl[1]+mid)*0.5f;
br[3]=点[3];
br[2]=(点[2]+点[3])*0.5f;
br[1]=(br[2]+mid)*0.5f;
br[0]=(br[1]+bl[2])*0.5f;
bl[3]=br[0];
返回BezierSingleLength(bl)+BezierSingleLength(br);
}

请参见。为什么您希望代码随时可用?您认为有人需要知道贝塞尔曲线的确切长度多久?如果您需要做一些以前从未做过的事情,请期待做一些工作。近似值不是确切长度..正在变..可用(除此之外,它的廉价配方可能在许多其他情况下可用)我有时用它来表示轨迹等等,知道给定轨迹的长度是件好事,我可能需要它,因为我的作者:这里最重要的问题是,你认为你需要近似值——而不是正确的——弧长,因为它决定了什么样的误差范围是可以接受的,以及你需要什么样的近似值甚至可以在不丢失您的信息的情况下获取needed@MarkRansom交流
//--------------- 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;
        }
    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)));
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;
    }
}
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);
}