/// <summary> /// Solves the equation \f$a0 + a1 x + a2 x^2 + a3 x^3 + a4 x^4 = 0\f$, returning all real values of /// \f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Quartic_function for the /// algorithm used. /// </summary> public static IEnumerable <double> Solve(double a0, double a1, double a2, double a3, double a4) { DebugUtil.AssertAllFinite(new double[] { a0, a1, a2, a3, a4 }, "a"); if (Math.Abs(a4) <= 0.005) { foreach (double v in CubicFunction.Solve(a0, a1, a2, a3)) { QuarticFunction f = new QuarticFunction(a0, a1, a2, a3, a4); yield return(f.NewtonRaphson(v)); } yield break; } double ba = a3 / a4; double ca = a2 / a4; double da = a1 / a4; // double ea = a0/a4; double p1 = a2 * a2 * a2 - 4.5 * a3 * a2 * a1 + 13.5 * a4 * a1 * a1 + 13.5 * a3 * a3 * a0 - 36.0 * a4 * a2 * a0; double q = a2 * a2 - 3.0 * a3 * a1 + 12.0 * a4 * a0; Complex p2 = p1 + Complex.Sqrt(-q * q * q + p1 * p1); Complex pow = Complex.Pow(p2, (1.0 / 3.0)); Complex p3 = q / (3.0 * a4 * pow) + pow / (3.0 * a4); Complex p4 = Complex.Sqrt(ba * ba / 4.0 - 2.0 * ca / (3.0) + p3); Complex p5 = a3 * a3 / (2.0 * a4 * a4) - 4.0 * a2 / (3.0 * a4) - p3; Complex p6 = (-ba * ba * ba + 4.0 * ba * ca - 8.0 * da) / (4.0 * p4); List <Complex> roots = new List <Complex> { -ba / (4.0) - p4 / 2.0 - 0.5 * Complex.Sqrt(p5 - p6), -ba / (4.0) - p4 / 2.0 + 0.5 * Complex.Sqrt(p5 - p6), -ba / (4.0) + p4 / 2.0 - 0.5 * Complex.Sqrt(p5 + p6), -ba / (4.0) + p4 / 2.0 + 0.5 * Complex.Sqrt(p5 + p6), }; foreach (Complex root in roots) { if (Math.Abs(root.Imaginary) <= 0.005) { DebugUtil.AssertFinite(root.Real, nameof(root.Real)); yield return(root.Real); } } }
/// <summary> /// Solves the equation \f$(q(x))^2 = b_0 + b_1 x + b_2 x^2 + b_3 x^3 + b_4 x^4\f$, returning all values of /// \f$x\f$ for which the equation is true. \f$q(x)\f$ is the quadratic spline. The _parameters z0 and c /// can be used to substitute x, such that \f$x = z0 + c t\f$. This is useful for raytracing. /// </summary> public override IEnumerable <double> SolveRaytrace(QuarticFunction surfaceFunction, double z0 = 0.0, double c = 1.0) { // Solve the polynomial equation for each segment: for (int i = 1; i < Points.Count; i++) { double x1 = Points.Key[i - 1]; double x2 = Points.Key[i]; double y1 = Points.Value[i - 1]; double y2 = Points.Value[i]; // Calculate and return the interpolated value: double dx = x2 - x1; double div = 1.0 / dx; double dy = y2 - y1; double a = -_parameters[i] * dx + dy; // Write in the form of a0 + a1 z + a2 z^2: double a0 = -a * x1 * x1 * div * div - (a + dy) * x1 * div + y1; double a1 = 2.0 * a * x1 * div * div + (a + dy) * div; double a2 = -a * div * div; // Substitute z = z0 + c t: double A0 = a0 + a1 * z0 + a2 * z0 * z0; double A1 = (a1 + 2.0 * a2 * z0) * c; double A2 = a2 * c * c; // Find the quartic polynomial to solve: double p0 = surfaceFunction.a0 - A0 * A0; double p1 = surfaceFunction.a1 - 2.0 * A0 * A1; double p2 = surfaceFunction.a2 - (2.0 * A0 * A2 + A1 * A1); double p3 = surfaceFunction.a3 - 2.0 * A1 * A2; double p4 = surfaceFunction.a4 - A2 * A2; // Solve the quartic polynomial: IEnumerable <double> intersections = QuarticFunction.Solve(p0, p1, p2, p3, p4); // Only return the value if it is sampled within the segment that we are currently considering, // otherwise the value we got is invalid: foreach (var j in intersections) { if (((z0 + c * j) > x1) && ((z0 + c * j) <= x2)) { yield return(j); } } } }
/// <summary> /// Solves the equation \f$(q(x))^2 = b_0 + b_1 x + b_2 x^2 + b_3 x^3 + b_4 x^4\f$, returning all values of /// \f$x\f$ for which the equation is true. \f$q(x)\f$ is the quadratic spline. The parameters z0 and c /// can be used to substitute x, such that \f$x = z0 + c t\f$. This is useful for raytracing. /// </summary> public override IEnumerable <float> SolveRaytrace(QuarticFunction surfaceFunction, float z0 = 0.0f, float c = 1.0f) { // Solve the polynomial equation for each segment: for (int i = 1; i < Points.Count; i++) { Real x1 = Points.Key[i - 1]; Real x2 = Points.Key[i]; Real y1 = Points.Value[i - 1]; Real y2 = Points.Value[i]; // Calculate and return the interpolated value: Real dx = x2 - x1; Real div = 1.0 / dx; Real dy = y2 - y1; Real a = -parameters[i] * dx + dy; // Write in the form of a0 + a1 z + a2 z^2: Real a0 = -a * x1 * x1 * div * div - (a + dy) * x1 * div + y1; Real a1 = 2.0 * a * x1 * div * div + (a + dy) * div; Real a2 = -a * div * div; // Substitute z = z0 + c t: Real A0 = a0 + a1 * z0 + a2 * z0 * z0; Real A1 = (a1 + 2.0 * a2 * z0) * c; Real A2 = a2 * c * c; // Find the quartic polynomial to solve: Real p0 = surfaceFunction.a0 - A0 * A0; Real p1 = surfaceFunction.a1 - 2.0 * A0 * A1; Real p2 = surfaceFunction.a2 - (2.0 * A0 * A2 + A1 * A1); Real p3 = surfaceFunction.a3 - 2.0 * A1 * A2; Real p4 = surfaceFunction.a4 - A2 * A2; // Solve the quartic polynomial: IEnumerable <float> intersections = QuarticFunction.Solve((float)p0, (float)p1, (float)p2, (float)p3, (float)p4); // Only return the value if it is sampled within the segment that we are currently considering, // otherwise the value we got is invalid: foreach (var j in intersections) { if (((z0 + c * j) > x1) && ((z0 + c * j) <= x2)) { yield return(j); } } } }
public IEnumerable <double> Roots() { return(QuarticFunction.Solve(A0, A1, A2, A3, A4)); }
/// <summary> /// Solves the equation \f$(q(x))^2 = b_0 + b_1 x + b_2 x^2 + b_3 x^3 + b_4 x^4\f$, returning all values of /// \f$x\f$ for which the equation is true. \f$q(x)\f$ is the continuous map. The parameters z0 and c /// can be used to substitute x, such that \f$x = z0 + c t\f$. This is useful for raytracing. /// </summary> public abstract IEnumerable <float> SolveRaytrace(QuarticFunction surfaceFunction, float z0 = 0.0f, float c = 1.0f);
public IEnumerable <double> Roots() { return(QuarticFunction.Solve(a0, a1, a2, a3, a4)); }
/// <summary> /// Solves the equation \f$(q(x))^2 = b_0 + b_1 x + b_2 x^2 + b_3 x^3 + b_4 x^4\f$, returning all values of /// \f$x\f$ for which the equation is true. \f$q(x)\f$ is the linear spline. The _parameters z0 and c /// can be used to substitute x, such that \f$x = z0 + c t\f$. This is useful for raytracing. /// </summary> public override IEnumerable <double> SolveRaytrace(QuarticFunction surfaceFunction, double z0 = 0.0, double c = 1.0) { throw new NotImplementedException(); return(null); }
/// <summary> /// Solves the equation \f$(q(x))^2 = b_0 + b_1 x + b_2 x^2 + b_3 x^3 + b_4 x^4\f$, returning all values of /// \f$x\f$ for which the equation is true. \f$q(x)\f$ is the continuous map. The parameters z0 and c /// can be used to substitute x, such that \f$x = z0 + c t\f$. This is useful for raytracing. /// </summary> public abstract IEnumerable <double> SolveRaytrace(QuarticFunction surfaceFunction, double z0 = 0.0, double c = 1.0);