1
0

bezier.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright (c), Recep Aslantas.
  3. *
  4. * MIT License (MIT), http://opensource.org/licenses/MIT
  5. * Full license can be found in the LICENSE file
  6. */
  7. #ifndef cglm_bezier_h
  8. #define cglm_bezier_h
  9. #include "common.h"
  10. #define GLM_BEZIER_MAT_INIT {{-1.0f, 3.0f, -3.0f, 1.0f}, \
  11. { 3.0f, -6.0f, 3.0f, 0.0f}, \
  12. {-3.0f, 3.0f, 0.0f, 0.0f}, \
  13. { 1.0f, 0.0f, 0.0f, 0.0f}}
  14. #define GLM_HERMITE_MAT_INIT {{ 2.0f, -3.0f, 0.0f, 1.0f}, \
  15. {-2.0f, 3.0f, 0.0f, 0.0f}, \
  16. { 1.0f, -2.0f, 1.0f, 0.0f}, \
  17. { 1.0f, -1.0f, 0.0f, 0.0f}}
  18. /* for C only */
  19. #define GLM_BEZIER_MAT ((mat4)GLM_BEZIER_MAT_INIT)
  20. #define GLM_HERMITE_MAT ((mat4)GLM_HERMITE_MAT_INIT)
  21. #define CGLM_DECASTEL_EPS 1e-9f
  22. #define CGLM_DECASTEL_MAX 1000
  23. #define CGLM_DECASTEL_SMALL 1e-20f
  24. /*!
  25. * @brief cubic bezier interpolation
  26. *
  27. * Formula:
  28. * B(s) = P0*(1-s)^3 + 3*C0*s*(1-s)^2 + 3*C1*s^2*(1-s) + P1*s^3
  29. *
  30. * similar result using matrix:
  31. * B(s) = glm_smc(t, GLM_BEZIER_MAT, (vec4){p0, c0, c1, p1})
  32. *
  33. * glm_eq(glm_smc(...), glm_bezier(...)) should return TRUE
  34. *
  35. * @param[in] s parameter between 0 and 1
  36. * @param[in] p0 begin point
  37. * @param[in] c0 control point 1
  38. * @param[in] c1 control point 2
  39. * @param[in] p1 end point
  40. *
  41. * @return B(s)
  42. */
  43. CGLM_INLINE
  44. float
  45. glm_bezier(float s, float p0, float c0, float c1, float p1) {
  46. float x, xx, ss, xs3, a;
  47. x = 1.0f - s;
  48. xx = x * x;
  49. ss = s * s;
  50. xs3 = (s - ss) * 3.0f;
  51. a = p0 * xx + c0 * xs3;
  52. return a + s * (c1 * xs3 + p1 * ss - a);
  53. }
  54. /*!
  55. * @brief cubic hermite interpolation
  56. *
  57. * Formula:
  58. * H(s) = P0*(2*s^3 - 3*s^2 + 1) + T0*(s^3 - 2*s^2 + s)
  59. * + P1*(-2*s^3 + 3*s^2) + T1*(s^3 - s^2)
  60. *
  61. * similar result using matrix:
  62. * H(s) = glm_smc(t, GLM_HERMITE_MAT, (vec4){p0, p1, c0, c1})
  63. *
  64. * glm_eq(glm_smc(...), glm_hermite(...)) should return TRUE
  65. *
  66. * @param[in] s parameter between 0 and 1
  67. * @param[in] p0 begin point
  68. * @param[in] t0 tangent 1
  69. * @param[in] t1 tangent 2
  70. * @param[in] p1 end point
  71. *
  72. * @return H(s)
  73. */
  74. CGLM_INLINE
  75. float
  76. glm_hermite(float s, float p0, float t0, float t1, float p1) {
  77. float ss, d, a, b, c, e, f;
  78. ss = s * s;
  79. a = ss + ss;
  80. c = a + ss;
  81. b = a * s;
  82. d = s * ss;
  83. f = d - ss;
  84. e = b - c;
  85. return p0 * (e + 1.0f) + t0 * (f - ss + s) + t1 * f - p1 * e;
  86. }
  87. /*!
  88. * @brief iterative way to solve cubic equation
  89. *
  90. * @param[in] prm parameter between 0 and 1
  91. * @param[in] p0 begin point
  92. * @param[in] c0 control point 1
  93. * @param[in] c1 control point 2
  94. * @param[in] p1 end point
  95. *
  96. * @return parameter to use in cubic equation
  97. */
  98. CGLM_INLINE
  99. float
  100. glm_decasteljau(float prm, float p0, float c0, float c1, float p1) {
  101. float u, v, a, b, c, d, e, f;
  102. int i;
  103. if (prm - p0 < CGLM_DECASTEL_SMALL)
  104. return 0.0f;
  105. if (p1 - prm < CGLM_DECASTEL_SMALL)
  106. return 1.0f;
  107. u = 0.0f;
  108. v = 1.0f;
  109. for (i = 0; i < CGLM_DECASTEL_MAX; i++) {
  110. /* de Casteljau Subdivision */
  111. a = (p0 + c0) * 0.5f;
  112. b = (c0 + c1) * 0.5f;
  113. c = (c1 + p1) * 0.5f;
  114. d = (a + b) * 0.5f;
  115. e = (b + c) * 0.5f;
  116. f = (d + e) * 0.5f; /* this one is on the curve! */
  117. /* The curve point is close enough to our wanted t */
  118. if (fabsf(f - prm) < CGLM_DECASTEL_EPS)
  119. return glm_clamp_zo((u + v) * 0.5f);
  120. /* dichotomy */
  121. if (f < prm) {
  122. p0 = f;
  123. c0 = e;
  124. c1 = c;
  125. u = (u + v) * 0.5f;
  126. } else {
  127. c0 = a;
  128. c1 = d;
  129. p1 = f;
  130. v = (u + v) * 0.5f;
  131. }
  132. }
  133. return glm_clamp_zo((u + v) * 0.5f);
  134. }
  135. #endif /* cglm_bezier_h */