寻找一条直线和三次样条曲线的交点

3

图表

我需要一种程序化的方法来查找一条从原点开始的直线f(x)与由4个点定义的三次样条曲线相交的点。该直线保证与样条曲线的中心线段在X1和X2之间相交。

我尝试了许多方法,但似乎无法获得预期结果。我怀疑问题出在我对复数的处理上。

有人能找到我的代码问题,或者建议其他方法吗?

    private Vector2 CubicInterpolatedIntersection(float y0, float y1, 
                            float y2, float y3, float lineSlope, lineYOffset)
    {
        // f(x) = lineSlope * x + lineYOffset
        // g(x) = (a0 * x^3) + (a1 * x^2) + (a2 * x) + a3

        // Calculate Catmull-Rom coefficients for g(x) equation as found 
        // in reference (1). These 
        double a0, a1, a2, a3;
        a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
        a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
        a2 = -0.5 * y0 + 0.5 * y2;
        a3 = y1;

        // To find POI, let 'g(x) - f(x) = 0'. Simplified equation is:
        // (a0 * x^3) + (a1 * x^2) + ((a2 - lineSlope) * x) 
        //                         + (a3 - lineYOffset) = 0

        // Put in standard form 'ax^3 + bx^2 + cx + d = 0'
        double a, b, c, d;
        a = a0;
        b = a1;
        c = a2 - lineSlope;
        d = a3 - lineYOffset;


        // Solve for roots using cubic equation as found in reference (2).
        //  x = {q + [q^2 + (r-p^2)^3]^(1/2)}^(1/3) 
        //         + {q - [q^2 + (r-p^2)^3]^(1/2)}^(1/3) + p
        //  Where...
        double p, q, r;
        p = -b / (3 * a);
        q = p * p * p + (b * c - 3 * a * d) / (6 * a * a);
        r = c / (3 * a);

        //Solve the equation starting from the center.
        double x, x2;
        x = r - p * p;
        x = x * x * x + q * q;
        // Here's where I'm not sure. The cubic equation contains a square 
        // root. So if x is negative at this point, then we need to proceed
        // with complex numbers.
        if (x >= 0)
        {
            x = Math.Sqrt(x);
            x = CubicRoot(q + x) + CubicRoot(q - x) + p;
        }
        else
        {
            x = Math.Sqrt(Math.Abs(x)); 
            // x now represents the imaginary component of 
            //                       a complex number 'a + b*i'
            // We need to take the cubic root of 'q + x' and 'q - x'
            // Since x is imaginary, we have two complex numbers in 
            // standard form. Therefore, we take the cubic root of 
            // the magnitude of these complex numbers
            x = CubicRoot(Math.Sqrt(q * q + x * x)) + 
                 CubicRoot(Math.Sqrt(q * q + -x * -x)) + p;
        }

        // At this point, x should hold the x-value of 
        //      the point of intersection.
        // Now use it to solve g(x).

        x2 = x * x;
        return new Vector2((float)Math.Abs(x), 
           (float)Math.Abs(a0 * x * x2 + a1 * x2 + a2 * x + a3));
    }

References:

  1. http://paulbourke.net/miscellaneous/interpolation/
  2. http://www.math.vanderbilt.edu/~schectex/courses/cubic/
2个回答

1

代码

if (x >= 0)
{  // One real root and two imaginaries.
    x = Math.Sqrt(x);
    x = CubicRoot(q + x) + CubicRoot(q - x) + p;
}
else
{  // Three real roots.
    x = Math.Sqrt(Math.Abs(x)); 


    x_1 = Math.Sign(q)*2*(q*q + x*x)^(1/6)*Math.Cos(Math.Atan(x/q)/3) + p;
    x_2 = Math.Sign(q)*2*(q*q + x*x)^(1/6)*Math.Cos(Math.Atan(x/q)/3 + Math.PI*2/3) + p;
    x_3 = Math.Sign(q)*2*(q*q + x*x)^(1/6)*Math.Cos(Math.Atan(x/q)/3 + Math.PI*4/3) + p;
}

你可以使用 Math.Pow( , 1/6) 或者 Math.Sqrt(CubicRoot( )) 或者 Math.Sqrt(Cbrt( )) 来计算 ( )^(1/6)。请参考Microsoft论坛上的讨论

q = 0 时要小心。 ( Atan(x/0) = pi/2 弧度。 Cos(Atan(x/0)/3) = Sqrt(3)/2 )


嗯...这似乎也不起作用。我使用4个均匀间隔且距离原点相等的点来定义g(x)进行了测试。然后我在每个pi/8间隔处检查了函数的结果。结果应该类似于正弦波,但实际上是不规则的、不连续的,并且始终为正数。也许还有其他问题? - Madison Brown
此外,我可以确定问题出现在这个函数中而不是程序的其他地方,因为当我将其替换为常规线性交点函数时,结果符合预期。 - Madison Brown
你能给我发送解决第三次多项式的函数吗?这个函数可以解决我测试过的所有多项式。 - Edoot
你可以测试其他解决方案(三阶有三个解决方案):x = Math.Sign(q)2(qq + xx)^(1/6)Math.Cos(Math.Atan(x/q)/3 + Math.PI2/3) + p;x = Math.Sign(q)2(qq + xx)^(1/6)Math.Cos(Math.Atan(x/q)/3 + Math.PI4/3) + p; - Edoot

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接