CatmullRomCurve.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. using UnityEngine;
  2. namespace ARLocation
  3. {
  4. /// <summary>
  5. /// A catmull-rom curve.
  6. /// </summary>
  7. public class CatmullRomCurve : Curve
  8. {
  9. // The control points
  10. private Vector3 p0;
  11. private Vector3 p1;
  12. private Vector3 p2;
  13. private Vector3 p3;
  14. // The alpha/tension factor
  15. private float alpha;
  16. // The knots
  17. private float t0;
  18. private float t1;
  19. private float t2;
  20. private float t3;
  21. // Getters for the knots
  22. public float T0 { get { return t0; } }
  23. public float T1 { get { return t1; } }
  24. public float T2 { get { return t2; } }
  25. public float T3 { get { return t3; } }
  26. /// <summary>
  27. /// The curve's cached length
  28. /// </summary>
  29. private float length;
  30. /// <summary>
  31. /// An array with the sample curve parameters.
  32. /// </summary>
  33. private float[] sampleParameters;
  34. /// <summary>
  35. /// An array with the curve's lenght at each of each sampled points.
  36. /// </summary>
  37. private float[] sampleLengths;
  38. /// <summary>
  39. /// Flag dirty sample.
  40. /// </summary>
  41. private bool isSampleDirty;
  42. /// <summary>
  43. /// The size of the last sample; used for cache validation.
  44. /// </summary>
  45. private int lastSampleSize = 100;
  46. /// <summary>
  47. /// Gets or sets the alpha.
  48. /// </summary>
  49. /// <value>The alpha.</value>
  50. public float Alpha
  51. {
  52. get
  53. {
  54. return alpha;
  55. }
  56. set
  57. {
  58. alpha = value;
  59. CalculateKnots();
  60. isSampleDirty = true;
  61. }
  62. }
  63. public Vector3 P0
  64. {
  65. get
  66. {
  67. return p0;
  68. }
  69. set
  70. {
  71. p0 = value;
  72. CalculateKnots();
  73. isSampleDirty = true;
  74. }
  75. }
  76. public Vector3 P1
  77. {
  78. get
  79. {
  80. return p1;
  81. }
  82. set
  83. {
  84. p1 = value;
  85. CalculateKnots();
  86. isSampleDirty = true;
  87. }
  88. }
  89. public Vector3 P2
  90. {
  91. get
  92. {
  93. return p2;
  94. }
  95. set
  96. {
  97. p2 = value;
  98. CalculateKnots();
  99. isSampleDirty = true;
  100. }
  101. }
  102. public Vector3 P3
  103. {
  104. get
  105. {
  106. return p3;
  107. }
  108. set
  109. {
  110. p3 = value;
  111. CalculateKnots();
  112. isSampleDirty = true;
  113. }
  114. }
  115. /// <summary>
  116. /// Creates a catmull-rom curve with control points p0, p1, p2 and p3, and with
  117. /// a given alpha/tension parameter.
  118. /// </summary>
  119. /// <param name="p0"></param>
  120. /// <param name="p1"></param>
  121. /// <param name="p2"></param>
  122. /// <param name="p3"></param>
  123. /// <param name="alpha"></param>
  124. public CatmullRomCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float alpha)
  125. {
  126. this.p0 = p0;
  127. this.p1 = p1;
  128. this.p2 = p2;
  129. this.p3 = p3;
  130. this.alpha = alpha;
  131. isSampleDirty = true;
  132. CalculateKnots();
  133. }
  134. private void CalculateKnots()
  135. {
  136. var x = alpha * 0.5f;
  137. t0 = 0.0f;
  138. t1 = t0 + Mathf.Pow((p1 - p0).sqrMagnitude, x);
  139. t2 = t1 + Mathf.Pow((p2 - p1).sqrMagnitude, x);
  140. t3 = t2 + Mathf.Pow((p3 - p2).sqrMagnitude, x);
  141. }
  142. /// <summary>
  143. /// Calculates the curve at a point u, where u is between 0 and 1.
  144. /// </summary>
  145. /// <param name="u">The curve parameter in the [0, 1] interval.</param>
  146. /// <returns></returns>
  147. public override Vector3 GetPoint(float u)
  148. {
  149. u = Mathf.Clamp(u, 0, 1);
  150. float t = t1 * (1 - u) + t2 * u;
  151. Vector3 a1 = (t1 - t) / (t1 - t0) * p0 + ((t - t0) / (t1 - t0)) * p1;
  152. Vector3 a2 = (t2 - t) / (t2 - t1) * p1 + ((t - t1) / (t2 - t1)) * p2;
  153. Vector3 a3 = (t3 - t) / (t3 - t2) * p2 + ((t - t2) / (t3 - t2)) * p3;
  154. Vector3 b1 = (t2 - t) / (t2 - t0) * a1 + ((t - t0) / (t2 - t0)) * a2;
  155. Vector3 b2 = (t3 - t) / (t3 - t1) * a2 + ((t - t1) / (t3 - t1)) * a3;
  156. Vector3 c = (t2 - t) / (t2 - t1) * b1 + ((t - t1) / (t2 - t1)) * b2;
  157. return c;
  158. }
  159. /// <summary>
  160. /// Calculates the point and the tangent of the curve.
  161. /// </summary>
  162. /// <param name="u">The curve parameter in the [0, 1] interval.</param>
  163. /// <returns></returns>
  164. public override CurvePointData GetPointAndTangent(float u)
  165. {
  166. u = Mathf.Clamp(u, 0, 1);
  167. float t = t1 * (1 - u) + t2 * u;
  168. Vector3 a1 = (t1 - t) / (t1 - t0) * p0 + ((t - t0) / (t1 - t0)) * p1;
  169. Vector3 a2 = (t2 - t) / (t2 - t1) * p1 + ((t - t1) / (t2 - t1)) * p2;
  170. Vector3 a3 = (t3 - t) / (t3 - t2) * p2 + ((t - t2) / (t3 - t2)) * p3;
  171. Vector3 b1 = (t2 - t) / (t2 - t0) * a1 + ((t - t0) / (t2 - t0)) * a2;
  172. Vector3 b2 = (t3 - t) / (t3 - t1) * a2 + ((t - t1) / (t3 - t1)) * a3;
  173. Vector3 c = (t2 - t) / (t2 - t1) * b1 + ((t - t1) / (t2 - t1)) * b2;
  174. Vector3 da1 = (1.0f / (t1 - t0)) * (p1 - p0);
  175. Vector3 da2 = (1.0f / (t2 - t1)) * (p2 - p1);
  176. Vector3 da3 = (1.0f / (t3 - t2)) * (p3 - p2);
  177. Vector3 db1 = (t2 - t) / (t2 - t0) * da1 + ((t - t0) / (t2 - t0)) * da2 +
  178. (1.0f / (t2 - t0)) * (a2 - a1);
  179. Vector3 db2 = (t3 - t) / (t3 - t1) * da2 + ((t - t1) / (t3 - t1)) * da3 +
  180. (1.0f / (t3 - t1)) * (a3 - a2);
  181. Vector3 dc = (t2 - t) / (t2 - t1) * db1 + ((t - t1) / (t2 - t1)) * db2 +
  182. (1.0f / (t2 - t1)) * (b2 - b1);
  183. return new CurvePointData { point = c, tangent = dc };
  184. }
  185. /// <summary>
  186. /// Creates a sample of (N+2) points (i.e., N + start and end points) of
  187. /// the current curve. Also calculates the length estimate.
  188. /// </summary>
  189. /// <returns>The sample.</returns>
  190. /// <param name="n">N.</param>
  191. public override Vector3[] Sample(int n)
  192. {
  193. lastSampleSize = n;
  194. if (n == 0)
  195. {
  196. return new[] { GetPoint(0), GetPoint(1) };
  197. }
  198. var sample = new Vector3[n + 2];
  199. var delta = 1.0f / (n + 1.0f);
  200. length = 0.0f;
  201. sampleParameters = new float[n + 2];
  202. sampleLengths = new float[n + 2];
  203. for (var i = 0; i < (n + 2); i++)
  204. {
  205. sample[i] = GetPoint(i * delta);
  206. sampleParameters[i] = i * delta;
  207. if (i > 0)
  208. {
  209. length += (sample[i] - sample[i - 1]).magnitude;
  210. }
  211. sampleLengths[i] = length;
  212. }
  213. isSampleDirty = false;
  214. return (Vector3[])sample.Clone();
  215. }
  216. /// <summary>
  217. /// Returns the estimated length.
  218. /// </summary>
  219. /// <returns>The length.</returns>
  220. /// <param name="n">N.</param>
  221. public override float EstimateLength(int n = 100)
  222. {
  223. if (isSampleDirty || lastSampleSize != n)
  224. {
  225. Sample(n);
  226. }
  227. return length;
  228. }
  229. /// <summary>
  230. /// Gets the curve parameter for a given length.
  231. /// </summary>
  232. /// <returns>The parameter for length.</returns>
  233. /// <param name="s">S.</param>
  234. public override float GetParameterForLength(float s)
  235. {
  236. if (isSampleDirty)
  237. {
  238. Sample(lastSampleSize);
  239. }
  240. for (var i = 0; i < (sampleParameters.Length - 1); i++)
  241. {
  242. if (s >= sampleLengths[i] && s <= sampleLengths[i + 1])
  243. {
  244. var a = (s - sampleLengths[i]) / (sampleLengths[i + 1] - sampleLengths[i]);
  245. return (1 - a) * sampleParameters[i] + a * sampleParameters[i + 1];
  246. }
  247. }
  248. return -1.0f;
  249. }
  250. /// <summary>
  251. /// Gets the curve point at a given length.
  252. /// </summary>
  253. /// <returns>The point at length.</returns>
  254. /// <param name="s">S.</param>
  255. public override Vector3 GetPointAtLength(float s)
  256. {
  257. return GetPoint(GetParameterForLength(s));
  258. }
  259. /// <summary>
  260. /// Gets the CurvePointData which stores the point and tangent
  261. /// at a given arc-length.
  262. /// </summary>
  263. /// <param name="s"></param>
  264. /// <returns></returns>
  265. public override CurvePointData GetPointAndTangentAtLength(float s)
  266. {
  267. return GetPointAndTangent(GetParameterForLength(s));
  268. }
  269. }
  270. }