bezier-solver.hpp 3.1 KB

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