equation-solver.cpp 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. #include "equation-solver.h"
  2. #define _USE_MATH_DEFINES
  3. #include <cmath>
  4. #define LARGE_RATIO ::msdfgen::real(1e10)
  5. #ifndef MSDFGEN_CUBE_ROOT
  6. #define MSDFGEN_CUBE_ROOT(x) pow((x), ::msdfgen::real(1)/::msdfgen::real(3))
  7. #endif
  8. namespace msdfgen {
  9. int solveQuadratic(real x[2], real a, real b, real c) {
  10. // a == 0 -> linear equation
  11. if (a == real(0) || fabs(b) > LARGE_RATIO*fabs(a)) {
  12. // a == 0, b == 0 -> no solution
  13. if (b == real(0)) {
  14. if (c == real(0))
  15. return -1; // 0 == 0
  16. return 0;
  17. }
  18. x[0] = -c/b;
  19. return 1;
  20. }
  21. real dscr = b*b-real(4)*a*c;
  22. if (dscr > real(0)) {
  23. dscr = sqrt(dscr);
  24. x[0] = (-b+dscr)/(a+a);
  25. x[1] = (-b-dscr)/(a+a);
  26. return 2;
  27. } else if (dscr == 0) {
  28. x[0] = -b/(a+a);
  29. return 1;
  30. } else
  31. return 0;
  32. }
  33. static int solveCubicNormed(real x[3], real a, real b, real c) {
  34. real a2 = a*a;
  35. real q = real(1)/real(9)*(a2-real(3)*b);
  36. real r = real(1)/real(54)*(a*(real(2)*a2-real(9)*b)+real(27)*c);
  37. real r2 = r*r;
  38. real q3 = q*q*q;
  39. a *= real(1)/real(3);
  40. if (r2 < q3) {
  41. real t = r/sqrt(q3);
  42. if (t < real(-1)) t = -1;
  43. if (t > real(1)) t = 1;
  44. t = real(1)/real(3)*acos(t);
  45. q = real(-2)*sqrt(q);
  46. x[0] = q*cos(t)-a;
  47. x[1] = q*cos(t+real(2)/real(3)*real(M_PI))-a;
  48. x[2] = q*cos(t-real(2)/real(3)*real(M_PI))-a;
  49. return 3;
  50. } else {
  51. real u = (r < real(0) ? real(1) : real(-1))*MSDFGEN_CUBE_ROOT(fabs(r)+sqrt(r2-q3));
  52. real v = u == real(0) ? real(0) : q/u;
  53. x[0] = (u+v)-a;
  54. if (u == v || LARGE_RATIO*fabs(u-v) < fabs(u+v)) {
  55. x[1] = real(-.5)*(u+v)-a;
  56. return 2;
  57. }
  58. return 1;
  59. }
  60. }
  61. int solveCubic(real x[3], real a, real b, real c, real d) {
  62. if (a != 0) {
  63. real bn = b/a;
  64. if (bn*bn < LARGE_RATIO) // Above this ratio, the numerical error gets larger than if we treated a as zero
  65. return solveCubicNormed(x, bn, c/a, d/a);
  66. }
  67. return solveQuadratic(x, b, c, d);
  68. }
  69. }