Clock.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import ClockRange from './ClockRange.js';
  2. import ClockStep from './ClockStep.js';
  3. import defaultValue from './defaultValue.js';
  4. import defined from './defined.js';
  5. import defineProperties from './defineProperties.js';
  6. import DeveloperError from './DeveloperError.js';
  7. import Event from './Event.js';
  8. import getTimestamp from './getTimestamp.js';
  9. import JulianDate from './JulianDate.js';
  10. /**
  11. * A simple clock for keeping track of simulated time.
  12. *
  13. * @alias Clock
  14. * @constructor
  15. *
  16. * @param {Object} [options] Object with the following properties:
  17. * @param {JulianDate} [options.startTime] The start time of the clock.
  18. * @param {JulianDate} [options.stopTime] The stop time of the clock.
  19. * @param {JulianDate} [options.currentTime] The current time.
  20. * @param {Number} [options.multiplier=1.0] Determines how much time advances when {@link Clock#tick} is called, negative values allow for advancing backwards.
  21. * @param {ClockStep} [options.clockStep=ClockStep.SYSTEM_CLOCK_MULTIPLIER] Determines if calls to {@link Clock#tick} are frame dependent or system clock dependent.
  22. * @param {ClockRange} [options.clockRange=ClockRange.UNBOUNDED] Determines how the clock should behave when {@link Clock#startTime} or {@link Clock#stopTime} is reached.
  23. * @param {Boolean} [options.canAnimate=true] Indicates whether {@link Clock#tick} can advance time. This could be false if data is being buffered, for example. The clock will only tick when both {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true.
  24. * @param {Boolean} [options.shouldAnimate=false] Indicates whether {@link Clock#tick} should attempt to advance time. The clock will only tick when both {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true.
  25. *
  26. * @exception {DeveloperError} startTime must come before stopTime.
  27. *
  28. *
  29. * @example
  30. * // Create a clock that loops on Christmas day 2013 and runs in real-time.
  31. * var clock = new Cesium.Clock({
  32. * startTime : Cesium.JulianDate.fromIso8601("2013-12-25"),
  33. * currentTime : Cesium.JulianDate.fromIso8601("2013-12-25"),
  34. * stopTime : Cesium.JulianDate.fromIso8601("2013-12-26"),
  35. * clockRange : Cesium.ClockRange.LOOP_STOP,
  36. * clockStep : Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER
  37. * });
  38. *
  39. * @see ClockStep
  40. * @see ClockRange
  41. * @see JulianDate
  42. */
  43. function Clock(options) {
  44. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  45. var currentTime = options.currentTime;
  46. var startTime = options.startTime;
  47. var stopTime = options.stopTime;
  48. if (!defined(currentTime)) {
  49. // if not specified, current time is the start time,
  50. // or if that is not specified, 1 day before the stop time,
  51. // or if that is not specified, then now.
  52. if (defined(startTime)) {
  53. currentTime = JulianDate.clone(startTime);
  54. } else if (defined(stopTime)) {
  55. currentTime = JulianDate.addDays(stopTime, -1.0, new JulianDate());
  56. } else {
  57. currentTime = JulianDate.now();
  58. }
  59. } else {
  60. currentTime = JulianDate.clone(currentTime);
  61. }
  62. if (!defined(startTime)) {
  63. // if not specified, start time is the current time
  64. // (as determined above)
  65. startTime = JulianDate.clone(currentTime);
  66. } else {
  67. startTime = JulianDate.clone(startTime);
  68. }
  69. if (!defined(stopTime)) {
  70. // if not specified, stop time is 1 day after the start time
  71. // (as determined above)
  72. stopTime = JulianDate.addDays(startTime, 1.0, new JulianDate());
  73. } else {
  74. stopTime = JulianDate.clone(stopTime);
  75. }
  76. //>>includeStart('debug', pragmas.debug);
  77. if (JulianDate.greaterThan(startTime, stopTime)) {
  78. throw new DeveloperError('startTime must come before stopTime.');
  79. }
  80. //>>includeEnd('debug');
  81. /**
  82. * The start time of the clock.
  83. * @type {JulianDate}
  84. */
  85. this.startTime = startTime;
  86. /**
  87. * The stop time of the clock.
  88. * @type {JulianDate}
  89. */
  90. this.stopTime = stopTime;
  91. /**
  92. * Determines how the clock should behave when
  93. * {@link Clock#startTime} or {@link Clock#stopTime}
  94. * is reached.
  95. * @type {ClockRange}
  96. * @default {@link ClockRange.UNBOUNDED}
  97. */
  98. this.clockRange = defaultValue(options.clockRange, ClockRange.UNBOUNDED);
  99. /**
  100. * Indicates whether {@link Clock#tick} can advance time. This could be false if data is being buffered,
  101. * for example. The clock will only advance time when both
  102. * {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true.
  103. * @type {Boolean}
  104. * @default true
  105. */
  106. this.canAnimate = defaultValue(options.canAnimate, true);
  107. /**
  108. * An {@link Event} that is fired whenever {@link Clock#tick} is called.
  109. * @type {Event}
  110. */
  111. this.onTick = new Event();
  112. /**
  113. * An {@link Event} that is fired whenever {@link Clock#stopTime} is reached.
  114. * @type {Event}
  115. */
  116. this.onStop = new Event();
  117. this._currentTime = undefined;
  118. this._multiplier = undefined;
  119. this._clockStep = undefined;
  120. this._shouldAnimate = undefined;
  121. this._lastSystemTime = getTimestamp();
  122. // set values using the property setters to
  123. // make values consistent.
  124. this.currentTime = currentTime;
  125. this.multiplier = defaultValue(options.multiplier, 1.0);
  126. this.shouldAnimate = defaultValue(options.shouldAnimate, false);
  127. this.clockStep = defaultValue(options.clockStep, ClockStep.SYSTEM_CLOCK_MULTIPLIER);
  128. }
  129. defineProperties(Clock.prototype, {
  130. /**
  131. * The current time.
  132. * Changing this property will change
  133. * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to
  134. * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}.
  135. * @memberof Clock.prototype
  136. * @type {JulianDate}
  137. */
  138. currentTime : {
  139. get : function() {
  140. return this._currentTime;
  141. },
  142. set : function(value) {
  143. if (JulianDate.equals(this._currentTime, value)) {
  144. return;
  145. }
  146. if (this._clockStep === ClockStep.SYSTEM_CLOCK) {
  147. this._clockStep = ClockStep.SYSTEM_CLOCK_MULTIPLIER;
  148. }
  149. this._currentTime = value;
  150. }
  151. },
  152. /**
  153. * Gets or sets how much time advances when {@link Clock#tick} is called. Negative values allow for advancing backwards.
  154. * If {@link Clock#clockStep} is set to {@link ClockStep.TICK_DEPENDENT}, this is the number of seconds to advance.
  155. * If {@link Clock#clockStep} is set to {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}, this value is multiplied by the
  156. * elapsed system time since the last call to {@link Clock#tick}.
  157. * Changing this property will change
  158. * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to
  159. * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}.
  160. * @memberof Clock.prototype
  161. * @type {Number}
  162. * @default 1.0
  163. */
  164. multiplier : {
  165. get : function() {
  166. return this._multiplier;
  167. },
  168. set : function(value) {
  169. if (this._multiplier === value) {
  170. return;
  171. }
  172. if (this._clockStep === ClockStep.SYSTEM_CLOCK) {
  173. this._clockStep = ClockStep.SYSTEM_CLOCK_MULTIPLIER;
  174. }
  175. this._multiplier = value;
  176. }
  177. },
  178. /**
  179. * Determines if calls to {@link Clock#tick} are frame dependent or system clock dependent.
  180. * Changing this property to {@link ClockStep.SYSTEM_CLOCK} will set
  181. * {@link Clock#multiplier} to 1.0, {@link Clock#shouldAnimate} to true, and
  182. * {@link Clock#currentTime} to the current system clock time.
  183. * @memberof Clock.prototype
  184. * @type ClockStep
  185. * @default {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}
  186. */
  187. clockStep : {
  188. get : function() {
  189. return this._clockStep;
  190. },
  191. set : function(value) {
  192. if (value === ClockStep.SYSTEM_CLOCK) {
  193. this._multiplier = 1.0;
  194. this._shouldAnimate = true;
  195. this._currentTime = JulianDate.now();
  196. }
  197. this._clockStep = value;
  198. }
  199. },
  200. /**
  201. * Indicates whether {@link Clock#tick} should attempt to advance time.
  202. * The clock will only advance time when both
  203. * {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true.
  204. * Changing this property will change
  205. * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to
  206. * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}.
  207. * @memberof Clock.prototype
  208. * @type {Boolean}
  209. * @default false
  210. */
  211. shouldAnimate : {
  212. get : function() {
  213. return this._shouldAnimate;
  214. },
  215. set : function(value) {
  216. if (this._shouldAnimate === value) {
  217. return;
  218. }
  219. if (this._clockStep === ClockStep.SYSTEM_CLOCK) {
  220. this._clockStep = ClockStep.SYSTEM_CLOCK_MULTIPLIER;
  221. }
  222. this._shouldAnimate = value;
  223. }
  224. }
  225. });
  226. /**
  227. * Advances the clock from the current time based on the current configuration options.
  228. * tick should be called every frame, regardless of whether animation is taking place
  229. * or not. To control animation, use the {@link Clock#shouldAnimate} property.
  230. *
  231. * @returns {JulianDate} The new value of the {@link Clock#currentTime} property.
  232. */
  233. Clock.prototype.tick = function() {
  234. var currentSystemTime = getTimestamp();
  235. var currentTime = JulianDate.clone(this._currentTime);
  236. if (this.canAnimate && this._shouldAnimate) {
  237. var clockStep = this._clockStep;
  238. if (clockStep === ClockStep.SYSTEM_CLOCK) {
  239. currentTime = JulianDate.now(currentTime);
  240. } else {
  241. var multiplier = this._multiplier;
  242. if (clockStep === ClockStep.TICK_DEPENDENT) {
  243. currentTime = JulianDate.addSeconds(currentTime, multiplier, currentTime);
  244. } else {
  245. var milliseconds = currentSystemTime - this._lastSystemTime;
  246. currentTime = JulianDate.addSeconds(currentTime, multiplier * (milliseconds / 1000.0), currentTime);
  247. }
  248. var clockRange = this.clockRange;
  249. var startTime = this.startTime;
  250. var stopTime = this.stopTime;
  251. if (clockRange === ClockRange.CLAMPED) {
  252. if (JulianDate.lessThan(currentTime, startTime)) {
  253. currentTime = JulianDate.clone(startTime, currentTime);
  254. } else if (JulianDate.greaterThan(currentTime, stopTime)) {
  255. currentTime = JulianDate.clone(stopTime, currentTime);
  256. this.onStop.raiseEvent(this);
  257. }
  258. } else if (clockRange === ClockRange.LOOP_STOP) {
  259. if (JulianDate.lessThan(currentTime, startTime)) {
  260. currentTime = JulianDate.clone(startTime, currentTime);
  261. }
  262. while (JulianDate.greaterThan(currentTime, stopTime)) {
  263. currentTime = JulianDate.addSeconds(startTime, JulianDate.secondsDifference(currentTime, stopTime), currentTime);
  264. this.onStop.raiseEvent(this);
  265. }
  266. }
  267. }
  268. }
  269. this._currentTime = currentTime;
  270. this._lastSystemTime = currentSystemTime;
  271. this.onTick.raiseEvent(this);
  272. return currentTime;
  273. };
  274. export default Clock;