All files / engine/Source/Core Clock.js

100% Statements 90/90
100% Branches 56/56
100% Functions 10/10
100% Lines 90/90

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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