Monado OpenXR Runtime
Loading...
Searching...
No Matches
cubic.h
Go to the documentation of this file.
1// Copyright 2019, Philipp Zabel
2// Copyright 2026, Beyley Cardellio
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Cubic root finder, specialized for P3P solving.
7 * @author Philipp Zabel <philipp.zabel@gmail.com>
8 * @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
9 * @ingroup xrt_iface
10 */
11
12#pragma once
13
14#include <math.h>
15
16
17/* Tuning variable for maximum loops and stopping
18 * criteria */
19#ifndef CUBIC_MAX_ITERS
20#define CUBIC_MAX_ITERS 100
21#endif
22#ifndef CUBIC_ROOT_EPSILON
23#define CUBIC_ROOT_EPSILON 1e-10
24#endif
25
26/*
27 * Given a cubic equation with coefficients b,c,d:
28 * f(x) = x^3 + bx^2 + cx + d
29 *
30 * return one root of the equation, preferably the
31 * one with the highest derivative. There is always
32 * at least 1 real root for such a cubic.
33 *
34 * We do that by choosing a starting point and using
35 * the Newton-Raphson method to find the root.
36 *
37 * The derivative of the cubic equation is:
38 * f'(x) = 3x^2 + 2bx + c
39 *
40 * Using the quadratic formula on the derivative, we can find
41 * the critical points:
42 * x = (-2*b ± sqrt(4*b^2 - 4*3*c) / 2*3
43 * x = (-b ± sqrt(b^2 - 3*c) / 3.0
44 *
45 * From the sqrt part, if b^2 < 3*c, there are no real critical
46 * points. If b^2 == 3*c, there is 1 critical point @ -b/3.0.
47 * In both these cases, the cubic is strictly monotonic, with only 1 root.
48 *
49 * Othwise, there are up to 3 roots, and we want the one
50 * with the larger derivative, so we choose a starting point
51 * as explained below.
52 *
53 * It's also worth remembering that given
54 * roots x1, x2, x3 then x1 + x2 + x3 = -b,
55 * which is why it is good to start at x = -b/3.0 for the
56 * single-root case.
57 *
58 * cubic b=0.000144 c=-2.460081 d=1.747825 -> r0 = 0.961102 f(r0) = 0.271356 (FAIL)
59 *
60 * t1 = 2.0736e-08
61 * t2 = -7.380243
62 */
63static inline double
64reduced_cubic_real_root(double b, double c, double d)
65{
66 double t1 = b * b;
67 double t2 = 3.0 * c;
68 double t3 = -b / 3.0;
69 double r0;
70
71 if (t1 <= t2) {
72 r0 = t3;
73 } else {
74 /* We need to decide which root to iterate toward,
75 * by starting to the left of the left critical point,
76 * the right of the right critical point, or in between
77 * the 2.
78 *
79 * We know the cubic is increasing, because coefficient a = 1,
80 * so the left critical point (cl) is a maxima, and the right (cr) is a minima.
81 *
82 * If f(cl) > 0, go left from there, else go right from cr.
83 */
84 double t4 = sqrt(t1 - t2) / 3.0;
85 double cl = t3 - t4;
86 double fx = cl * (cl * (cl + b) + c) + d;
87 if (fx > 0) {
88 r0 = cl - t4 / 4.0;
89 } else {
90 double cr = t3 + t4;
91 r0 = cr + t4 / 4.0;
92 }
93 }
94
95 /* Newton-Raphson - r0 = r0 - f(r0)/f'(r0) */
96 int i = CUBIC_MAX_ITERS;
97 do {
98 double fx = r0 * (r0 * (r0 + b) + c) + d;
99 double dx = fx / (r0 * (3 * r0 + 2 * b) + c);
100
101 /* If we converged, exit or if the delta gets really small, break out too,
102 * because we're not moving any closer */
103 if (fabs(fx) < CUBIC_ROOT_EPSILON || fabs(dx) < CUBIC_ROOT_EPSILON) {
104 return r0;
105 }
106
107 r0 -= dx;
108 } while (i-- > 0);
109
110 return r0;
111}