QuarticRealPolynomial.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import CubicRealPolynomial from './CubicRealPolynomial.js';
  2. import DeveloperError from './DeveloperError.js';
  3. import CesiumMath from './Math.js';
  4. import QuadraticRealPolynomial from './QuadraticRealPolynomial.js';
  5. /**
  6. * Defines functions for 4th order polynomial functions of one variable with only real coefficients.
  7. *
  8. * @exports QuarticRealPolynomial
  9. */
  10. var QuarticRealPolynomial = {};
  11. /**
  12. * Provides the discriminant of the quartic equation from the supplied coefficients.
  13. *
  14. * @param {Number} a The coefficient of the 4th order monomial.
  15. * @param {Number} b The coefficient of the 3rd order monomial.
  16. * @param {Number} c The coefficient of the 2nd order monomial.
  17. * @param {Number} d The coefficient of the 1st order monomial.
  18. * @param {Number} e The coefficient of the 0th order monomial.
  19. * @returns {Number} The value of the discriminant.
  20. */
  21. QuarticRealPolynomial.computeDiscriminant = function(a, b, c, d, e) {
  22. //>>includeStart('debug', pragmas.debug);
  23. if (typeof a !== 'number') {
  24. throw new DeveloperError('a is a required number.');
  25. }
  26. if (typeof b !== 'number') {
  27. throw new DeveloperError('b is a required number.');
  28. }
  29. if (typeof c !== 'number') {
  30. throw new DeveloperError('c is a required number.');
  31. }
  32. if (typeof d !== 'number') {
  33. throw new DeveloperError('d is a required number.');
  34. }
  35. if (typeof e !== 'number') {
  36. throw new DeveloperError('e is a required number.');
  37. }
  38. //>>includeEnd('debug');
  39. var a2 = a * a;
  40. var a3 = a2 * a;
  41. var b2 = b * b;
  42. var b3 = b2 * b;
  43. var c2 = c * c;
  44. var c3 = c2 * c;
  45. var d2 = d * d;
  46. var d3 = d2 * d;
  47. var e2 = e * e;
  48. var e3 = e2 * e;
  49. var discriminant = (b2 * c2 * d2 - 4.0 * b3 * d3 - 4.0 * a * c3 * d2 + 18 * a * b * c * d3 - 27.0 * a2 * d2 * d2 + 256.0 * a3 * e3) +
  50. e * (18.0 * b3 * c * d - 4.0 * b2 * c3 + 16.0 * a * c2 * c2 - 80.0 * a * b * c2 * d - 6.0 * a * b2 * d2 + 144.0 * a2 * c * d2) +
  51. e2 * (144.0 * a * b2 * c - 27.0 * b2 * b2 - 128.0 * a2 * c2 - 192.0 * a2 * b * d);
  52. return discriminant;
  53. };
  54. function original(a3, a2, a1, a0) {
  55. var a3Squared = a3 * a3;
  56. var p = a2 - 3.0 * a3Squared / 8.0;
  57. var q = a1 - a2 * a3 / 2.0 + a3Squared * a3 / 8.0;
  58. var r = a0 - a1 * a3 / 4.0 + a2 * a3Squared / 16.0 - 3.0 * a3Squared * a3Squared / 256.0;
  59. // Find the roots of the cubic equations: h^6 + 2 p h^4 + (p^2 - 4 r) h^2 - q^2 = 0.
  60. var cubicRoots = CubicRealPolynomial.computeRealRoots(1.0, 2.0 * p, p * p - 4.0 * r, -q * q);
  61. if (cubicRoots.length > 0) {
  62. var temp = -a3 / 4.0;
  63. // Use the largest positive root.
  64. var hSquared = cubicRoots[cubicRoots.length - 1];
  65. if (Math.abs(hSquared) < CesiumMath.EPSILON14) {
  66. // y^4 + p y^2 + r = 0.
  67. var roots = QuadraticRealPolynomial.computeRealRoots(1.0, p, r);
  68. if (roots.length === 2) {
  69. var root0 = roots[0];
  70. var root1 = roots[1];
  71. var y;
  72. if (root0 >= 0.0 && root1 >= 0.0) {
  73. var y0 = Math.sqrt(root0);
  74. var y1 = Math.sqrt(root1);
  75. return [temp - y1, temp - y0, temp + y0, temp + y1];
  76. } else if (root0 >= 0.0 && root1 < 0.0) {
  77. y = Math.sqrt(root0);
  78. return [temp - y, temp + y];
  79. } else if (root0 < 0.0 && root1 >= 0.0) {
  80. y = Math.sqrt(root1);
  81. return [temp - y, temp + y];
  82. }
  83. }
  84. return [];
  85. } else if (hSquared > 0.0) {
  86. var h = Math.sqrt(hSquared);
  87. var m = (p + hSquared - q / h) / 2.0;
  88. var n = (p + hSquared + q / h) / 2.0;
  89. // Now solve the two quadratic factors: (y^2 + h y + m)(y^2 - h y + n);
  90. var roots1 = QuadraticRealPolynomial.computeRealRoots(1.0, h, m);
  91. var roots2 = QuadraticRealPolynomial.computeRealRoots(1.0, -h, n);
  92. if (roots1.length !== 0) {
  93. roots1[0] += temp;
  94. roots1[1] += temp;
  95. if (roots2.length !== 0) {
  96. roots2[0] += temp;
  97. roots2[1] += temp;
  98. if (roots1[1] <= roots2[0]) {
  99. return [roots1[0], roots1[1], roots2[0], roots2[1]];
  100. } else if (roots2[1] <= roots1[0]) {
  101. return [roots2[0], roots2[1], roots1[0], roots1[1]];
  102. } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) {
  103. return [roots2[0], roots1[0], roots1[1], roots2[1]];
  104. } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) {
  105. return [roots1[0], roots2[0], roots2[1], roots1[1]];
  106. } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) {
  107. return [roots2[0], roots1[0], roots2[1], roots1[1]];
  108. }
  109. return [roots1[0], roots2[0], roots1[1], roots2[1]];
  110. }
  111. return roots1;
  112. }
  113. if (roots2.length !== 0) {
  114. roots2[0] += temp;
  115. roots2[1] += temp;
  116. return roots2;
  117. }
  118. return [];
  119. }
  120. }
  121. return [];
  122. }
  123. function neumark(a3, a2, a1, a0) {
  124. var a1Squared = a1 * a1;
  125. var a2Squared = a2 * a2;
  126. var a3Squared = a3 * a3;
  127. var p = -2.0 * a2;
  128. var q = a1 * a3 + a2Squared - 4.0 * a0;
  129. var r = a3Squared * a0 - a1 * a2 * a3 + a1Squared;
  130. var cubicRoots = CubicRealPolynomial.computeRealRoots(1.0, p, q, r);
  131. if (cubicRoots.length > 0) {
  132. // Use the most positive root
  133. var y = cubicRoots[0];
  134. var temp = (a2 - y);
  135. var tempSquared = temp * temp;
  136. var g1 = a3 / 2.0;
  137. var h1 = temp / 2.0;
  138. var m = tempSquared - 4.0 * a0;
  139. var mError = tempSquared + 4.0 * Math.abs(a0);
  140. var n = a3Squared - 4.0 * y;
  141. var nError = a3Squared + 4.0 * Math.abs(y);
  142. var g2;
  143. var h2;
  144. if (y < 0.0 || (m * nError < n * mError)) {
  145. var squareRootOfN = Math.sqrt(n);
  146. g2 = squareRootOfN / 2.0;
  147. h2 = squareRootOfN === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfN;
  148. } else {
  149. var squareRootOfM = Math.sqrt(m);
  150. g2 = squareRootOfM === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfM;
  151. h2 = squareRootOfM / 2.0;
  152. }
  153. var G;
  154. var g;
  155. if (g1 === 0.0 && g2 === 0.0) {
  156. G = 0.0;
  157. g = 0.0;
  158. } else if (CesiumMath.sign(g1) === CesiumMath.sign(g2)) {
  159. G = g1 + g2;
  160. g = y / G;
  161. } else {
  162. g = g1 - g2;
  163. G = y / g;
  164. }
  165. var H;
  166. var h;
  167. if (h1 === 0.0 && h2 === 0.0) {
  168. H = 0.0;
  169. h = 0.0;
  170. } else if (CesiumMath.sign(h1) === CesiumMath.sign(h2)) {
  171. H = h1 + h2;
  172. h = a0 / H;
  173. } else {
  174. h = h1 - h2;
  175. H = a0 / h;
  176. }
  177. // Now solve the two quadratic factors: (y^2 + G y + H)(y^2 + g y + h);
  178. var roots1 = QuadraticRealPolynomial.computeRealRoots(1.0, G, H);
  179. var roots2 = QuadraticRealPolynomial.computeRealRoots(1.0, g, h);
  180. if (roots1.length !== 0) {
  181. if (roots2.length !== 0) {
  182. if (roots1[1] <= roots2[0]) {
  183. return [roots1[0], roots1[1], roots2[0], roots2[1]];
  184. } else if (roots2[1] <= roots1[0]) {
  185. return [roots2[0], roots2[1], roots1[0], roots1[1]];
  186. } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) {
  187. return [roots2[0], roots1[0], roots1[1], roots2[1]];
  188. } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) {
  189. return [roots1[0], roots2[0], roots2[1], roots1[1]];
  190. } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) {
  191. return [roots2[0], roots1[0], roots2[1], roots1[1]];
  192. }
  193. return [roots1[0], roots2[0], roots1[1], roots2[1]];
  194. }
  195. return roots1;
  196. }
  197. if (roots2.length !== 0) {
  198. return roots2;
  199. }
  200. }
  201. return [];
  202. }
  203. /**
  204. * Provides the real valued roots of the quartic polynomial with the provided coefficients.
  205. *
  206. * @param {Number} a The coefficient of the 4th order monomial.
  207. * @param {Number} b The coefficient of the 3rd order monomial.
  208. * @param {Number} c The coefficient of the 2nd order monomial.
  209. * @param {Number} d The coefficient of the 1st order monomial.
  210. * @param {Number} e The coefficient of the 0th order monomial.
  211. * @returns {Number[]} The real valued roots.
  212. */
  213. QuarticRealPolynomial.computeRealRoots = function(a, b, c, d, e) {
  214. //>>includeStart('debug', pragmas.debug);
  215. if (typeof a !== 'number') {
  216. throw new DeveloperError('a is a required number.');
  217. }
  218. if (typeof b !== 'number') {
  219. throw new DeveloperError('b is a required number.');
  220. }
  221. if (typeof c !== 'number') {
  222. throw new DeveloperError('c is a required number.');
  223. }
  224. if (typeof d !== 'number') {
  225. throw new DeveloperError('d is a required number.');
  226. }
  227. if (typeof e !== 'number') {
  228. throw new DeveloperError('e is a required number.');
  229. }
  230. //>>includeEnd('debug');
  231. if (Math.abs(a) < CesiumMath.EPSILON15) {
  232. return CubicRealPolynomial.computeRealRoots(b, c, d, e);
  233. }
  234. var a3 = b / a;
  235. var a2 = c / a;
  236. var a1 = d / a;
  237. var a0 = e / a;
  238. var k = (a3 < 0.0) ? 1 : 0;
  239. k += (a2 < 0.0) ? k + 1 : k;
  240. k += (a1 < 0.0) ? k + 1 : k;
  241. k += (a0 < 0.0) ? k + 1 : k;
  242. switch (k) {
  243. case 0:
  244. return original(a3, a2, a1, a0);
  245. case 1:
  246. return neumark(a3, a2, a1, a0);
  247. case 2:
  248. return neumark(a3, a2, a1, a0);
  249. case 3:
  250. return original(a3, a2, a1, a0);
  251. case 4:
  252. return original(a3, a2, a1, a0);
  253. case 5:
  254. return neumark(a3, a2, a1, a0);
  255. case 6:
  256. return original(a3, a2, a1, a0);
  257. case 7:
  258. return original(a3, a2, a1, a0);
  259. case 8:
  260. return neumark(a3, a2, a1, a0);
  261. case 9:
  262. return original(a3, a2, a1, a0);
  263. case 10:
  264. return original(a3, a2, a1, a0);
  265. case 11:
  266. return neumark(a3, a2, a1, a0);
  267. case 12:
  268. return original(a3, a2, a1, a0);
  269. case 13:
  270. return original(a3, a2, a1, a0);
  271. case 14:
  272. return original(a3, a2, a1, a0);
  273. case 15:
  274. return original(a3, a2, a1, a0);
  275. default:
  276. return undefined;
  277. }
  278. };
  279. export default QuarticRealPolynomial;