Spline.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. //
  2. // Copyright (c) 2008-2020 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Core/Spline.h"
  24. #include "../IO/Log.h"
  25. namespace Urho3D
  26. {
  27. const char* interpolationModeNames[] =
  28. {
  29. "Bezier",
  30. "Catmull-Rom",
  31. "Linear",
  32. "Catmull-Rom Full",
  33. nullptr
  34. };
  35. Spline::Spline() :
  36. interpolationMode_(BEZIER_CURVE)
  37. {
  38. }
  39. Spline::Spline(InterpolationMode mode) :
  40. interpolationMode_(mode)
  41. {
  42. }
  43. Spline::Spline(const Vector<Variant>& knots, InterpolationMode mode) :
  44. interpolationMode_(mode),
  45. knots_(knots)
  46. {
  47. }
  48. Variant Spline::GetPoint(float f) const
  49. {
  50. if (knots_.Size() < 2)
  51. return knots_.Size() == 1 ? knots_[0] : Variant::EMPTY;
  52. if (f > 1.f)
  53. f = 1.f;
  54. else if (f < 0.f)
  55. f = 0.f;
  56. switch (interpolationMode_)
  57. {
  58. case BEZIER_CURVE:
  59. return BezierInterpolation(knots_, f);
  60. case CATMULL_ROM_CURVE:
  61. return CatmullRomInterpolation(knots_, f);
  62. case LINEAR_CURVE:
  63. return LinearInterpolation(knots_, f);
  64. case CATMULL_ROM_FULL_CURVE:
  65. {
  66. /// \todo Do not allocate a new vector each time
  67. Vector<Variant> fullKnots;
  68. if (knots_.Size() > 1)
  69. {
  70. // Non-cyclic case: duplicate start and end
  71. if (knots_.Front() != knots_.Back())
  72. {
  73. fullKnots.Push(knots_.Front());
  74. fullKnots.Push(knots_);
  75. fullKnots.Push(knots_.Back());
  76. }
  77. // Cyclic case: smooth the tangents
  78. else
  79. {
  80. fullKnots.Push(knots_[knots_.Size() - 2]);
  81. fullKnots.Push(knots_);
  82. fullKnots.Push(knots_[1]);
  83. }
  84. }
  85. return CatmullRomInterpolation(fullKnots, f);
  86. }
  87. default:
  88. URHO3D_LOGERROR("Unsupported interpolation mode");
  89. return Variant::EMPTY;
  90. }
  91. }
  92. void Spline::SetKnot(const Variant& knot, unsigned index)
  93. {
  94. if (index < knots_.Size())
  95. {
  96. if (knots_.Size() > 0 && knots_[0].GetType() == knot.GetType())
  97. knots_[index] = knot;
  98. else if (knots_.Empty())
  99. knots_.Push(knot);
  100. else
  101. URHO3D_LOGERRORF("Attempted to set a Spline's Knot value of type %s where elements are already using %s",
  102. knot.GetTypeName().CString(), knots_[0].GetTypeName().CString());
  103. }
  104. }
  105. void Spline::AddKnot(const Variant& knot)
  106. {
  107. if (knots_.Size() > 0 && knots_[0].GetType() == knot.GetType())
  108. knots_.Push(knot);
  109. else if (knots_.Empty())
  110. knots_.Push(knot);
  111. else
  112. URHO3D_LOGERRORF("Attempted to add Knot to Spline of type %s where elements are already using %s", knot.GetTypeName().CString(),
  113. knots_[0].GetTypeName().CString());
  114. }
  115. void Spline::AddKnot(const Variant& knot, unsigned index)
  116. {
  117. if (index > knots_.Size())
  118. index = knots_.Size();
  119. if (knots_.Size() > 0 && knots_[0].GetType() == knot.GetType())
  120. knots_.Insert(index, knot);
  121. else if (knots_.Empty())
  122. knots_.Push(knot);
  123. else
  124. URHO3D_LOGERRORF("Attempted to add Knot to Spline of type %s where elements are already using %s", knot.GetTypeName().CString(),
  125. knots_[0].GetTypeName().CString());
  126. }
  127. Variant Spline::BezierInterpolation(const Vector<Variant>& knots, float t) const
  128. {
  129. if (knots.Size() == 2)
  130. {
  131. switch (knots[0].GetType())
  132. {
  133. case VAR_FLOAT:
  134. case VAR_VECTOR2:
  135. case VAR_VECTOR3:
  136. case VAR_VECTOR4:
  137. case VAR_COLOR:
  138. case VAR_DOUBLE:
  139. return LinearInterpolation(knots[0], knots[1], t);
  140. default:
  141. return Variant::EMPTY;
  142. }
  143. }
  144. else
  145. {
  146. /// \todo Do not allocate a new vector each time
  147. Vector<Variant> interpolatedKnots;
  148. for (unsigned i = 1; i < knots.Size(); i++)
  149. {
  150. switch (knots[0].GetType())
  151. {
  152. case VAR_FLOAT:
  153. case VAR_VECTOR2:
  154. case VAR_VECTOR3:
  155. case VAR_VECTOR4:
  156. case VAR_COLOR:
  157. case VAR_DOUBLE:
  158. interpolatedKnots.Push(LinearInterpolation(knots[i - 1], knots[i], t));
  159. break;
  160. default:
  161. return Variant::EMPTY;
  162. }
  163. }
  164. return BezierInterpolation(interpolatedKnots, t);
  165. }
  166. }
  167. template <typename T> Variant CalculateCatmullRom(const T& p0, const T& p1, const T& p2, const T& p3, float t, float t2, float t3)
  168. {
  169. return Variant(0.5f * ((2.0f * p1) + (-p0 + p2) * t +
  170. (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 +
  171. (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3));
  172. }
  173. Variant Spline::CatmullRomInterpolation(const Vector<Variant>& knots, float t) const
  174. {
  175. if (knots.Size() < 4)
  176. return Variant::EMPTY;
  177. else
  178. {
  179. if (t >= 1.f)
  180. return knots[knots.Size() - 2];
  181. auto originIndex = static_cast<int>(t * (knots.Size() - 3));
  182. t = fmodf(t * (knots.Size() - 3), 1.f);
  183. float t2 = t * t;
  184. float t3 = t2 * t;
  185. switch (knots[originIndex].GetType())
  186. {
  187. case VAR_FLOAT:
  188. return CalculateCatmullRom(knots[originIndex].GetFloat(), knots[originIndex + 1].GetFloat(),
  189. knots[originIndex + 2].GetFloat(), knots[originIndex + 3].GetFloat(), t, t2, t3);
  190. case VAR_VECTOR2:
  191. return CalculateCatmullRom(knots[originIndex].GetVector2(), knots[originIndex + 1].GetVector2(),
  192. knots[originIndex + 2].GetVector2(), knots[originIndex + 3].GetVector2(), t, t2, t3);
  193. case VAR_VECTOR3:
  194. return CalculateCatmullRom(knots[originIndex].GetVector3(), knots[originIndex + 1].GetVector3(),
  195. knots[originIndex + 2].GetVector3(), knots[originIndex + 3].GetVector3(), t, t2, t3);
  196. case VAR_VECTOR4:
  197. return CalculateCatmullRom(knots[originIndex].GetVector4(), knots[originIndex + 1].GetVector4(),
  198. knots[originIndex + 2].GetVector4(), knots[originIndex + 3].GetVector4(), t, t2, t3);
  199. case VAR_COLOR:
  200. return CalculateCatmullRom(knots[originIndex].GetColor(), knots[originIndex + 1].GetColor(),
  201. knots[originIndex + 2].GetColor(), knots[originIndex + 3].GetColor(), t, t2, t3);
  202. case VAR_DOUBLE:
  203. return CalculateCatmullRom(knots[originIndex].GetDouble(), knots[originIndex + 1].GetDouble(),
  204. knots[originIndex + 2].GetDouble(), knots[originIndex + 3].GetDouble(), t, t2, t3);
  205. default:
  206. return Variant::EMPTY;
  207. }
  208. }
  209. }
  210. Variant Spline::LinearInterpolation(const Vector<Variant>& knots, float t) const
  211. {
  212. if (knots.Size() < 2)
  213. return Variant::EMPTY;
  214. else
  215. {
  216. if (t >= 1.f)
  217. return knots.Back();
  218. int originIndex = Clamp((int)(t * (knots.Size() - 1)), 0, (int)(knots.Size() - 2));
  219. t = fmodf(t * (knots.Size() - 1), 1.f);
  220. return LinearInterpolation(knots[originIndex], knots[originIndex + 1], t);
  221. }
  222. }
  223. Variant Spline::LinearInterpolation(const Variant& lhs, const Variant& rhs, float t) const
  224. {
  225. switch (lhs.GetType())
  226. {
  227. case VAR_FLOAT:
  228. return Lerp(lhs.GetFloat(), rhs.GetFloat(), t);
  229. case VAR_VECTOR2:
  230. return lhs.GetVector2().Lerp(rhs.GetVector2(), t);
  231. case VAR_VECTOR3:
  232. return lhs.GetVector3().Lerp(rhs.GetVector3(), t);
  233. case VAR_VECTOR4:
  234. return lhs.GetVector4().Lerp(rhs.GetVector4(), t);
  235. case VAR_COLOR:
  236. return lhs.GetColor().Lerp(rhs.GetColor(), t);
  237. case VAR_DOUBLE:
  238. return Lerp(lhs.GetDouble(), rhs.GetDouble(), t);
  239. default:
  240. return Variant::EMPTY;
  241. }
  242. }
  243. }