/*global define*/ define(['Core/defined', 'Core/DeveloperError', 'Core/freezeObject', 'Core/JulianDate', 'Core/TimeStandard'], function( defined, DeveloperError, freezeObject, JulianDate, TimeStandard) { "use strict"; /** * An interval defined by a start date and a stop date. The end points are optionally included * in the interval. The interval should be treated as immutable. * * @alias TimeInterval * @constructor * * @param {JulianDate} start The start date of the interval. * @param {JulianDate} stop The stop date of the interval. * @param {Boolean} [isStartIncluded=true] <code>true</code> if the start date is included in the interval, <code>false</code> otherwise. * @param {Boolean} [isStopIncluded=true] <code>true</code> if the stop date is included in the interval, <code>false</code> otherwise. * @param {Object} [data The data associated with this interval. * * @exception {DeveloperError} start must be specified. * @exception {DeveloperError} stop must be specified. * * @see TimeInterval.fromIso8601 * @see TimeIntervalCollection * @see JulianDate * * @example * // Construct an Timeinterval closed on one end with a Color payload. * var interval = new TimeInterval(JulianDate.fromTotalDays(1000), JulianDate.fromTotalDays(1001), true, false, Color.WHITE); */ var TimeInterval = function(start, stop, isStartIncluded, isStopIncluded, data) { if (!defined(start)) { throw new DeveloperError('start must be specified.'); } if (!defined(stop)) { throw new DeveloperError('stop must be specified.'); } if (!defined(isStartIncluded)) { isStartIncluded = true; } if (!defined(isStopIncluded)) { isStopIncluded = true; } var stopComparedToStart = JulianDate.compare(stop, start); /** * The start time of the interval. */ this.start = start; /** * The stop time of the interval. */ this.stop = stop; /** * The data associated with this interval. */ this.data = data; /** * Indicates if <code>start</code> is included in the interval or not. */ this.isStartIncluded = isStartIncluded; /** * Indicates if <code>stop</code> is included in the interval or not. */ this.isStopIncluded = isStopIncluded; /** * Indicates if the interval is empty. */ this.isEmpty = stopComparedToStart < 0 || (stopComparedToStart === 0 && (!isStartIncluded || !isStopIncluded)); }; /** * Creates an immutable TimeInterval from an ISO 8601 interval string. * * @memberof TimeInterval * * @param {String} iso8601String A valid ISO8601 interval. * @param {Boolean} [isStartIncluded=true] <code>true</code> if the start date is included in the interval, <code>false</code> otherwise. * @param {Boolean} [isStopIncluded=true] <code>true</code> if the stop date is included in the interval, <code>false</code> otherwise. * @param {Object} [data] The data associated with this interval. * * @returns {TimeInterval} The new {@Link TimeInterval} instance or <code>undefined</code> if an invalid ISO8601 string is provided. * * @see TimeInterval * @see TimeIntervalCollection * @see JulianDate * @see <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601 on Wikipedia</a>. * * @example * // Construct an open Timeinterval with a Cartesian data payload. * var interval = TimeInterval.fromIso8601('2012-03-15T11:02:24.55Z/2012-03-15T12:28:24.03Z', false, false, new Cartesian3(1,2,3)); */ TimeInterval.fromIso8601 = function(iso8601String, isStartIncluded, isStopIncluded, data) { var iso8601Interval = iso8601String.split('/'); var intervalStart = JulianDate.fromIso8601(iso8601Interval[0]); var intervalStop = JulianDate.fromIso8601(iso8601Interval[1]); return new TimeInterval(intervalStart, intervalStop, isStartIncluded, isStopIncluded, data); }; /** * Compares the provided TimeIntervals and returns * <code>true</code> if they are equal, <code>false</code> otherwise. * @memberof TimeInterval * * @param {TimeInterval} [left] The first interval. * @param {TimeInterval} [right] The second interval. * @param {Function} [dataComparer] A function which compares the data of the two intervals. If ommitted, reference equality is used. * * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. */ TimeInterval.equals = function(left, right, dataComparer) { return left === right || defined(left) && defined(right) && (left.isEmpty && right.isEmpty || left.isStartIncluded === right.isStartIncluded && left.isStopIncluded === right.isStopIncluded && JulianDate.equals(left.start, right.start) && JulianDate.equals(left.stop, right.stop) && (left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data)))); }; /** * Compares the provided TimeIntervals componentwise and returns * <code>true</code> if they are within the provided epsilon, * <code>false</code> otherwise. * @memberof TimeInterval * * @param {TimeInterval} [left] The first TimeInterval. * @param {TimeInterval} [right] The second TimeInterval. * @param {Number} epsilon The epsilon to use for equality testing. * @param {Function} [dataComparer] A function which compares the data of the two intervals. If ommitted, reference equality is used. * * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. * * @exception {DeveloperError} epsilon is required and must be number. */ TimeInterval.equalsEpsilon = function(left, right, epsilon, dataComparer) { if (typeof epsilon !== 'number') { throw new DeveloperError('epsilon is required and must be a number.'); } return left === right || defined(left) && defined(right) && (left.isEmpty && right.isEmpty || left.isStartIncluded === right.isStartIncluded && left.isStopIncluded === right.isStopIncluded && JulianDate.equalsEpsilon(left.start, right.start, epsilon) && JulianDate.equalsEpsilon(left.stop, right.stop, epsilon) && (left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data)))); }; /** * Creates a copy of this TimeInterval. * * @returns A new TimeInterval that is equal to this interval. * * @memberof TimeInterval */ TimeInterval.prototype.clone = function() { return new TimeInterval(this.start, this.stop, this.isStartIncluded, this.isStopIncluded, this.data); }; /** * An empty interval. * * @memberof TimeInterval */ TimeInterval.EMPTY = freezeObject(new TimeInterval(new JulianDate(0, 0, TimeStandard.TAI), new JulianDate(0, 0, TimeStandard.TAI), false, false)); /** * Computes an interval which is the intersection of this interval with another while * also providing a means to merge the data of the two intervals. * * @param {TimeInterval} other The interval to intersect with this interval. * @param {Function} [mergeCallback] A callback which takes the data property from * both intervals as input and merges it into a single new value. If the callback is undefined, * this will intersect the two intervals and return the new interval with the data from this * interval. * * @returns {TimeInterval} The new {@Link TimeInterval} that is the intersection of the two intervals, * with its data representing the merge of the data in the two existing intervals. */ TimeInterval.prototype.intersect = function(other, mergeCallback) { if (!defined(other)) { return TimeInterval.EMPTY; } var otherStart = other.start; var otherStop = other.stop; var otherIsStartIncluded = other.isStartIncluded; var otherIsStopIncluded = other.isStopIncluded; var thisStart = this.start; var thisStop = this.stop; var thisIsStartIncluded = this.isStartIncluded; var thisIsStopIncluded = this.isStopIncluded; var outputData; var isStartIncluded; var isStopIncluded; if (otherStart.greaterThanOrEquals(thisStart) && thisStop.greaterThanOrEquals(otherStart)) { isStartIncluded = (!JulianDate.equals(otherStart, thisStart) && otherIsStartIncluded) || (thisIsStartIncluded && otherIsStartIncluded); isStopIncluded = thisIsStopIncluded && otherIsStopIncluded; outputData = defined(mergeCallback) ? mergeCallback(this.data, other.data) : this.data; if (thisStop.greaterThanOrEquals(otherStop)) { isStopIncluded = isStopIncluded || (!JulianDate.equals(otherStop, thisStop) && otherIsStopIncluded); return new TimeInterval(otherStart, otherStop, isStartIncluded, isStopIncluded, outputData); } isStopIncluded = isStopIncluded || thisIsStopIncluded; return new TimeInterval(otherStart, thisStop, isStartIncluded, isStopIncluded, outputData); } if (otherStart.lessThanOrEquals(thisStart) && thisStart.lessThanOrEquals(otherStop)) { isStartIncluded = (JulianDate.equals(otherStart, thisStart) === false && thisIsStartIncluded) || (thisIsStartIncluded && otherIsStartIncluded); isStopIncluded = thisIsStopIncluded && otherIsStopIncluded; outputData = defined(mergeCallback) ? mergeCallback(this.data, other.data) : this.data; if (thisStop.greaterThanOrEquals(otherStop)) { isStopIncluded = isStopIncluded || (JulianDate.equals(otherStop, thisStop) === false && otherIsStopIncluded); return new TimeInterval(thisStart, otherStop, isStartIncluded, isStopIncluded, outputData); } isStopIncluded = isStopIncluded || thisIsStopIncluded; return new TimeInterval(thisStart, thisStop, isStartIncluded, isStopIncluded, outputData); } return TimeInterval.EMPTY; }; /** * Returns <code>true</code> if this interval contains the specified date. * * @memberof TimeInterval * * @param {JulianDate} date The date to check for. * * @returns {Boolean} <code>true</code> if the TimeInterval contains the specified date, <code>false</code> otherwise. */ TimeInterval.prototype.contains = function(date) { if (this.isEmpty) { return false; } var startComparedToDate = JulianDate.compare(this.start, date); // if (start == date) if (startComparedToDate === 0) { return this.isStartIncluded; } var dateComparedToStop = JulianDate.compare(date, this.stop); // if (date == stop) if (dateComparedToStop === 0) { return this.isStopIncluded; } // return start < date && date < stop return startComparedToDate < 0 && dateComparedToStop < 0; }; /** * Compares this TimeInterval against the provided TimeInterval componentwise and returns * <code>true</code> if they are equal, <code>false</code> otherwise. * @memberof TimeInterval * * @param {TimeInterval} [right] The right hand side Cartesian. * @param {Function} [dataComparer] A function which compares the data of the two intervals. If ommitted, reference equality is used. * * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. */ TimeInterval.prototype.equals = function(other, dataComparer) { return TimeInterval.equals(this, other, dataComparer); }; /** * Compares this TimeInterval against the provided TimeInterval componentwise and returns * <code>true</code> if they are within the provided epsilon, * <code>false</code> otherwise. * @memberof TimeInterval * * @param {TimeInterval} [right] The right hand side Cartesian. * @param {Number} epsilon The epsilon to use for equality testing. * @param {Function} [dataComparer] A function which compares the data of the two intervals. If ommitted, reference equality is used. * * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. * * @exception {DeveloperError} epsilon is required and must be a number. */ TimeInterval.prototype.equalsEpsilon = function(other, epsilon, dataComparer) { return TimeInterval.equalsEpsilon(this, other, epsilon, dataComparer); }; return TimeInterval; });