Spline.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import Cartesian3 from "./Cartesian3.js";
  2. import Check from "./Check.js";
  3. import CesiumMath from "./Math.js";
  4. import defaultValue from "./defaultValue.js";
  5. import DeveloperError from "./DeveloperError.js";
  6. import Quaternion from "./Quaternion.js";
  7. /**
  8. * Creates a curve parameterized and evaluated by time. This type describes an interface
  9. * and is not intended to be instantiated directly.
  10. *
  11. * @alias Spline
  12. * @constructor
  13. *
  14. * @see CatmullRomSpline
  15. * @see LinearSpline
  16. * @see HermiteSpline
  17. * @see QuaternionSpline
  18. * @see MorphWeightSpline
  19. */
  20. function Spline() {
  21. /**
  22. * An array of times for the control points.
  23. * @type {Number[]}
  24. * @default undefined
  25. */
  26. this.times = undefined;
  27. /**
  28. * An array of control points.
  29. * @type {Cartesian3[]|Quaternion[]}
  30. * @default undefined
  31. */
  32. this.points = undefined;
  33. DeveloperError.throwInstantiationError();
  34. }
  35. /**
  36. * Gets the type of the point. This helps a spline determine how to interpolate
  37. * and return its values.
  38. *
  39. * @param {Number|Cartesian3|Quaternion} point
  40. * @returns {*} The type of the point.
  41. *
  42. * @exception {DeveloperError} value must be a Cartesian3, Quaternion, or Number.
  43. *
  44. * @private
  45. */
  46. Spline.getPointType = function (point) {
  47. if (typeof point === "number") {
  48. return Number;
  49. }
  50. if (point instanceof Cartesian3) {
  51. return Cartesian3;
  52. }
  53. if (point instanceof Quaternion) {
  54. return Quaternion;
  55. }
  56. //>>includeStart('debug', pragmas.debug);
  57. throw new DeveloperError(
  58. "point must be a Cartesian3, Quaternion, or Number."
  59. );
  60. //>>includeEnd('debug');
  61. };
  62. /**
  63. * Evaluates the curve at a given time.
  64. * @function
  65. *
  66. * @param {Number} time The time at which to evaluate the curve.
  67. * @param {Cartesian3|Quaternion|Number[]} [result] The object onto which to store the result.
  68. * @returns {Cartesian3|Quaternion|Number[]} The modified result parameter or a new instance of the point on the curve at the given time.
  69. *
  70. * @exception {DeveloperError} time must be in the range <code>[t<sub>0</sub>, t<sub>n</sub>]</code>, where <code>t<sub>0</sub></code>
  71. * is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element
  72. * in the array <code>times</code>.
  73. */
  74. Spline.prototype.evaluate = DeveloperError.throwInstantiationError;
  75. /**
  76. * Finds an index <code>i</code> in <code>times</code> such that the parameter
  77. * <code>time</code> is in the interval <code>[times[i], times[i + 1]]</code>.
  78. *
  79. * @param {Number} time The time.
  80. * @param {Number} startIndex The index from which to start the search.
  81. * @returns {Number} The index for the element at the start of the interval.
  82. *
  83. * @exception {DeveloperError} time must be in the range <code>[t<sub>0</sub>, t<sub>n</sub>]</code>, where <code>t<sub>0</sub></code>
  84. * is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element
  85. * in the array <code>times</code>.
  86. */
  87. Spline.prototype.findTimeInterval = function (time, startIndex) {
  88. const times = this.times;
  89. const length = times.length;
  90. //>>includeStart('debug', pragmas.debug);
  91. Check.typeOf.number("time", time);
  92. if (time < times[0] || time > times[length - 1]) {
  93. throw new DeveloperError("time is out of range.");
  94. }
  95. //>>includeEnd('debug');
  96. // Take advantage of temporal coherence by checking current, next and previous intervals
  97. // for containment of time.
  98. startIndex = defaultValue(startIndex, 0);
  99. if (time >= times[startIndex]) {
  100. if (startIndex + 1 < length && time < times[startIndex + 1]) {
  101. return startIndex;
  102. } else if (startIndex + 2 < length && time < times[startIndex + 2]) {
  103. return startIndex + 1;
  104. }
  105. } else if (startIndex - 1 >= 0 && time >= times[startIndex - 1]) {
  106. return startIndex - 1;
  107. }
  108. // The above failed so do a linear search. For the use cases so far, the
  109. // length of the list is less than 10. In the future, if there is a bottle neck,
  110. // it might be here.
  111. let i;
  112. if (time > times[startIndex]) {
  113. for (i = startIndex; i < length - 1; ++i) {
  114. if (time >= times[i] && time < times[i + 1]) {
  115. break;
  116. }
  117. }
  118. } else {
  119. for (i = startIndex - 1; i >= 0; --i) {
  120. if (time >= times[i] && time < times[i + 1]) {
  121. break;
  122. }
  123. }
  124. }
  125. if (i === length - 1) {
  126. i = length - 2;
  127. }
  128. return i;
  129. };
  130. /**
  131. * Wraps the given time to the period covered by the spline.
  132. * @function
  133. *
  134. * @param {Number} time The time.
  135. * @return {Number} The time, wrapped around the animation period.
  136. */
  137. Spline.prototype.wrapTime = function (time) {
  138. //>>includeStart('debug', pragmas.debug);
  139. Check.typeOf.number("time", time);
  140. //>>includeEnd('debug');
  141. const times = this.times;
  142. const timeEnd = times[times.length - 1];
  143. const timeStart = times[0];
  144. const timeStretch = timeEnd - timeStart;
  145. let divs;
  146. if (time < timeStart) {
  147. divs = Math.floor((timeStart - time) / timeStretch) + 1;
  148. time += divs * timeStretch;
  149. }
  150. if (time > timeEnd) {
  151. divs = Math.floor((time - timeEnd) / timeStretch) + 1;
  152. time -= divs * timeStretch;
  153. }
  154. return time;
  155. };
  156. /**
  157. * Clamps the given time to the period covered by the spline.
  158. * @function
  159. *
  160. * @param {Number} time The time.
  161. * @return {Number} The time, clamped to the animation period.
  162. */
  163. Spline.prototype.clampTime = function (time) {
  164. //>>includeStart('debug', pragmas.debug);
  165. Check.typeOf.number("time", time);
  166. //>>includeEnd('debug');
  167. const times = this.times;
  168. return CesiumMath.clamp(time, times[0], times[times.length - 1]);
  169. };
  170. export default Spline;