You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
10 KiB
JavaScript

import ClockRange from "./ClockRange.js";
import ClockStep from "./ClockStep.js";
import defaultValue from "./defaultValue.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.
* var 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 = defaultValue(options, defaultValue.EMPTY_OBJECT);
var currentTime = options.currentTime;
var startTime = options.startTime;
var 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 = defaultValue(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 = defaultValue(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 = defaultValue(options.multiplier, 1.0);
this.shouldAnimate = defaultValue(options.shouldAnimate, false);
this.clockStep = defaultValue(
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 () {
var currentSystemTime = getTimestamp();
var currentTime = JulianDate.clone(this._currentTime);
if (this.canAnimate && this._shouldAnimate) {
var clockStep = this._clockStep;
if (clockStep === ClockStep.SYSTEM_CLOCK) {
currentTime = JulianDate.now(currentTime);
} else {
var multiplier = this._multiplier;
if (clockStep === ClockStep.TICK_DEPENDENT) {
currentTime = JulianDate.addSeconds(
currentTime,
multiplier,
currentTime
);
} else {
var milliseconds = currentSystemTime - this._lastSystemTime;
currentTime = JulianDate.addSeconds(
currentTime,
multiplier * (milliseconds / 1000.0),
currentTime
);
}
var clockRange = this.clockRange;
var startTime = this.startTime;
var 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;