bezier-solver.hpp 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #pragma once
  2. #include <cmath>
  3. #include "Vector2.hpp"
  4. // Parameters for iterative search of closest point on a cubic Bezier curve. Increase for higher precision.
  5. #define MSDFGEN_CUBIC_SEARCH_STARTS 4
  6. #define MSDFGEN_CUBIC_SEARCH_STEPS 4
  7. #define MSDFGEN_QUADRATIC_RATIO_LIMIT 1e8
  8. #ifndef MSDFGEN_CUBE_ROOT
  9. #define MSDFGEN_CUBE_ROOT(x) pow((x), 1/3.)
  10. #endif
  11. namespace msdfgen {
  12. /**
  13. * Returns the parameter for the quadratic Bezier curve (P0, P1, P2) for the point closest to point P. May be outside the (0, 1) range.
  14. * p = P-P0
  15. * q = 2*P1-2*P0
  16. * r = P2-2*P1+P0
  17. */
  18. inline double quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2 r) {
  19. double qq = q.squaredLength();
  20. double rr = r.squaredLength();
  21. if (qq >= MSDFGEN_QUADRATIC_RATIO_LIMIT*rr)
  22. return dotProduct(p, q)/qq;
  23. double norm = .5/rr;
  24. double a = 3*norm*dotProduct(q, r);
  25. double b = norm*(qq-2*dotProduct(p, r));
  26. double c = norm*dotProduct(p, q);
  27. double aa = a*a;
  28. double g = 1/9.*(aa-3*b);
  29. double h = 1/54.*(a*(aa+aa-9*b)-27*c);
  30. double hh = h*h;
  31. double ggg = g*g*g;
  32. a *= 1/3.;
  33. if (hh < ggg) {
  34. double u = 1/3.*acos(h/sqrt(ggg));
  35. g = -2*sqrt(g);
  36. if (h >= 0) {
  37. double t = g*cos(u)-a;
  38. if (t >= 0)
  39. return t;
  40. return g*cos(u+2.0943951023931954923)-a; // 2.094 = PI*2/3
  41. } else {
  42. double t = g*cos(u+2.0943951023931954923)-a;
  43. if (t <= 1)
  44. return t;
  45. return g*cos(u)-a;
  46. }
  47. }
  48. double s = (h < 0 ? 1. : -1.)*MSDFGEN_CUBE_ROOT(fabs(h)+sqrt(hh-ggg));
  49. return s+g/s-a;
  50. }
  51. /**
  52. * Returns the parameter for the cubic Bezier curve (P0, P1, P2, P3) for the point closest to point P. Squared distance is provided as optional output parameter.
  53. * p = P-P0
  54. * q = 3*P1-3*P0
  55. * r = 3*P2-6*P1+3*P0
  56. * s = P3-3*P2+3*P1-P0
  57. */
  58. inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s, double &squaredDistance) {
  59. squaredDistance = p.squaredLength();
  60. double bestT = 0;
  61. for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
  62. double t = 1./MSDFGEN_CUBIC_SEARCH_STARTS*i;
  63. Vector2 curP = p-(q+(r+s*t)*t)*t;
  64. for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
  65. Vector2 d0 = q+(r+r+3*s*t)*t;
  66. Vector2 d1 = r+r+6*s*t;
  67. t += dotProduct(curP, d0)/(d0.squaredLength()-dotProduct(curP, d1));
  68. if (t <= 0 || t >= 1)
  69. break;
  70. curP = p-(q+(r+s*t)*t)*t;
  71. double curSquaredDistance = curP.squaredLength();
  72. if (curSquaredDistance < squaredDistance) {
  73. squaredDistance = curSquaredDistance;
  74. bestT = t;
  75. }
  76. }
  77. }
  78. return bestT;
  79. }
  80. inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s) {
  81. double squaredDistance;
  82. return cubicNearPoint(p, q, r, s, squaredDistance);
  83. }
  84. }