diff --git a/CHANGES.md b/CHANGES.md index c545f41ccab3..d3eaf2696966 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ Change Log ========== +### 1.45 - 2018-05-01 + +##### Fixes :wrench: +* Fixed bugs in `TimeIntervalCollection.removeInterval`. [#6418](https://github.com/AnalyticalGraphicsInc/cesium/pull/6418). + ### 1.44 - 2018-04-02 ##### Highlights :sparkler: diff --git a/Source/Core/TimeIntervalCollection.js b/Source/Core/TimeIntervalCollection.js index 2f3715fb16a3..d0570400a4fc 100644 --- a/Source/Core/TimeIntervalCollection.js +++ b/Source/Core/TimeIntervalCollection.js @@ -316,8 +316,6 @@ define([ return; } - var comparison; - var index; var intervals = this._intervals; // Handle the common case quickly: we're adding a new interval which is after all existing intervals. @@ -328,43 +326,59 @@ define([ } // Keep the list sorted by the start date - index = binarySearch(intervals, interval, compareIntervalStartTimes); + var index = binarySearch(intervals, interval, compareIntervalStartTimes); if (index < 0) { index = ~index; - } else if (index > 0 && interval.isStartIncluded && intervals[index - 1].isStartIncluded && intervals[index - 1].start.equals(interval.start)) { + } else { // interval's start date exactly equals the start date of at least one interval in the collection. // It could actually equal the start date of two intervals if one of them does not actually // include the date. In that case, the binary search could have found either. We need to // look at the surrounding intervals and their IsStartIncluded properties in order to make sure // we're working with the correct interval. - --index; - } else if (index < intervals.length && !interval.isStartIncluded && intervals[index].isStartIncluded && intervals[index].start.equals(interval.start)) { - ++index; + + // eslint-disable-next-line no-lonely-if + if (index > 0 && + interval.isStartIncluded && + intervals[index - 1].isStartIncluded && + intervals[index - 1].start.equals(interval.start)) { + + --index; + } else if (index < intervals.length && + !interval.isStartIncluded && + intervals[index].isStartIncluded && + intervals[index].start.equals(interval.start)) { + + ++index; + } } + var comparison; if (index > 0) { // Not the first thing in the list, so see if the interval before this one // overlaps this one. + comparison = JulianDate.compare(intervals[index - 1].stop, interval.start); - if (comparison > 0 || (comparison === 0 && (intervals[index - 1].isStopIncluded || interval.isStartIncluded))) { + if (comparison > 0 || + (comparison === 0 && + (intervals[index - 1].isStopIncluded || interval.isStartIncluded))) { // There is an overlap if (defined(dataComparer) ? dataComparer(intervals[index - 1].data, interval.data) : (intervals[index - 1].data === interval.data)) { // Overlapping intervals have the same data, so combine them if (JulianDate.greaterThan(interval.stop, intervals[index - 1].stop)) { interval = new TimeInterval({ - start : intervals[index - 1].start, - stop : interval.stop, - isStartIncluded : intervals[index - 1].isStartIncluded, - isStopIncluded : interval.isStopIncluded, - data : interval.data + start: intervals[index - 1].start, + stop: interval.stop, + isStartIncluded: intervals[index - 1].isStartIncluded, + isStopIncluded: interval.isStopIncluded, + data: interval.data }); } else { interval = new TimeInterval({ - start : intervals[index - 1].start, - stop : intervals[index - 1].stop, - isStartIncluded : intervals[index - 1].isStartIncluded, - isStopIncluded : intervals[index - 1].isStopIncluded || (interval.stop.equals(intervals[index - 1].stop) && interval.isStopIncluded), - data : interval.data + start: intervals[index - 1].start, + stop: intervals[index - 1].stop, + isStartIncluded: intervals[index - 1].isStartIncluded, + isStopIncluded: intervals[index - 1].isStopIncluded || (interval.stop.equals(intervals[index - 1].stop) && interval.isStopIncluded), + data: interval.data }); } intervals.splice(index - 1, 1); @@ -375,29 +389,24 @@ define([ // If the existing interval extends past the end of the new one, // split the existing interval into two intervals. comparison = JulianDate.compare(intervals[index - 1].stop, interval.stop); - if (comparison > 0 || (comparison === 0 && intervals[index - 1].isStopIncluded && !interval.isStopIncluded)) { - intervals.splice(index - 1, 1, new TimeInterval({ - start : intervals[index - 1].start, - stop : interval.start, - isStartIncluded : intervals[index - 1].isStartIncluded, - isStopIncluded : !interval.isStartIncluded, - data : intervals[index - 1].data - }), new TimeInterval({ - start : interval.stop, - stop : intervals[index - 1].stop, - isStartIncluded : !interval.isStopIncluded, - isStopIncluded : intervals[index - 1].isStopIncluded, - data : intervals[index - 1].data + if (comparison > 0 || + (comparison === 0 && intervals[index - 1].isStopIncluded && !interval.isStopIncluded)) { + + intervals.splice(index, 0, new TimeInterval({ + start: interval.stop, + stop: intervals[index - 1].stop, + isStartIncluded: !interval.isStopIncluded, + isStopIncluded: intervals[index - 1].isStopIncluded, + data: intervals[index - 1].data })); - } else { - intervals[index - 1] = new TimeInterval({ - start : intervals[index - 1].start, - stop : interval.start, - isStartIncluded : intervals[index - 1].isStartIncluded, - isStopIncluded : !interval.isStartIncluded, - data : intervals[index - 1].data - }); } + intervals[index - 1] = new TimeInterval({ + start: intervals[index - 1].start, + stop: interval.start, + isStartIncluded: intervals[index - 1].isStartIncluded, + isStopIncluded: !interval.isStartIncluded, + data: intervals[index - 1].data + }); } } } @@ -405,28 +414,30 @@ define([ while (index < intervals.length) { // Not the last thing in the list, so see if the intervals after this one overlap this one. comparison = JulianDate.compare(interval.stop, intervals[index].start); - if (comparison > 0 || (comparison === 0 && (interval.isStopIncluded || intervals[index].isStartIncluded))) { + if (comparison > 0 || + (comparison === 0 && (interval.isStopIncluded || intervals[index].isStartIncluded))) { // There is an overlap if (defined(dataComparer) ? dataComparer(intervals[index].data, interval.data) : intervals[index].data === interval.data) { // Overlapping intervals have the same data, so combine them interval = new TimeInterval({ - start : interval.start, - stop : JulianDate.greaterThan(intervals[index].stop, interval.stop) ? intervals[index].stop : interval.stop, - isStartIncluded : interval.isStartIncluded, - isStopIncluded : JulianDate.greaterThan(intervals[index].stop, interval.stop) ? intervals[index].isStopIncluded : interval.isStopIncluded, - data : interval.data + start: interval.start, + stop: JulianDate.greaterThan(intervals[index].stop, interval.stop) ? intervals[index].stop : interval.stop, + isStartIncluded: interval.isStartIncluded, + isStopIncluded: JulianDate.greaterThan(intervals[index].stop, interval.stop) ? intervals[index].isStopIncluded : interval.isStopIncluded, + data: interval.data }); intervals.splice(index, 1); } else { // Overlapping intervals have different data. The new interval // being added 'wins' so truncate the next interval. intervals[index] = new TimeInterval({ - start : interval.stop, - stop : intervals[index].stop, - isStartIncluded : !interval.isStopIncluded, - isStopIncluded : intervals[index].isStopIncluded, - data : intervals[index].data + start: interval.stop, + stop: intervals[index].stop, + isStartIncluded: !interval.isStopIncluded, + isStopIncluded: intervals[index].isStopIncluded, + data: intervals[index].data }); + if (intervals[index].isEmpty) { intervals.splice(index, 1); } else { @@ -464,7 +475,6 @@ define([ return false; } - var result = false; var intervals = this._intervals; var index = binarySearch(intervals, interval, compareIntervalStartTimes); @@ -472,96 +482,89 @@ define([ index = ~index; } - var intervalStart = interval.start; - var intervalStop = interval.stop; - var intervalIsStartIncluded = interval.isStartIncluded; - var intervalIsStopIncluded = interval.isStopIncluded; + var result = false; // Check for truncation of the end of the previous interval. - if (index > 0) { - var indexMinus1 = intervals[index - 1]; - var indexMinus1Stop = indexMinus1.stop; - if (JulianDate.greaterThan(indexMinus1Stop, intervalStart) || - (TimeInterval.equals(indexMinus1Stop, intervalStart) && - indexMinus1.isStopIncluded && intervalIsStartIncluded)) { - result = true; - - if (JulianDate.greaterThan(indexMinus1Stop, intervalStop) || - (indexMinus1.isStopIncluded && !intervalIsStopIncluded && TimeInterval.equals(indexMinus1Stop, intervalStop))) { - // Break the existing interval into two pieces - intervals.splice(index, 0, new TimeInterval({ - start : intervalStop, - stop : indexMinus1Stop, - isStartIncluded : !intervalIsStopIncluded, - isStopIncluded : indexMinus1.isStopIncluded, - data : indexMinus1.data - })); - } - intervals[index - 1] = new TimeInterval({ - start : indexMinus1.start, - stop : intervalStart, - isStartIncluded : indexMinus1.isStartIncluded, - isStopIncluded : !intervalIsStartIncluded, - data : indexMinus1.data - }); + if (index > 0 && + (JulianDate.greaterThan(intervals[index - 1].stop, interval.start) || + (intervals[index - 1].stop.equals(interval.start) && intervals[index - 1].isStopIncluded && interval.isStartIncluded))) { + + result = true; + + if (JulianDate.greaterThan(intervals[index - 1].stop, interval.stop) || + (intervals[index - 1].isStopIncluded && !interval.isStopIncluded && intervals[index - 1].stop.equals(interval.stop))) { + // Break the existing interval into two pieces + intervals.splice(index, 0, new TimeInterval({ + start: interval.stop, + stop: intervals[index - 1].stop, + isStartIncluded: !interval.isStopIncluded, + isStopIncluded: intervals[index - 1].isStopIncluded, + data: intervals[index - 1].data + })); } + intervals[index - 1] = new TimeInterval({ + start: intervals[index - 1].start, + stop: interval.start, + isStartIncluded: intervals[index - 1].isStartIncluded, + isStopIncluded: !interval.isStartIncluded, + data: intervals[index - 1].data + }); } // Check if the Start of the current interval should remain because interval.start is the same but // it is not included. - var indexInterval = intervals[index]; if (index < intervals.length && - !intervalIsStartIncluded && - indexInterval.isStartIncluded && - intervalStart.equals(indexInterval.start)) { + !interval.isStartIncluded && + intervals[index].isStartIncluded && + interval.start.equals(intervals[index].start)) { + result = true; intervals.splice(index, 0, new TimeInterval({ - start : indexInterval.start, - stop : indexInterval.start, - isStartIncluded : true, - isStopIncluded : true, - data : indexInterval.data + start: intervals[index].start, + stop: intervals[index].start, + isStartIncluded: true, + isStopIncluded: true, + data: intervals[index].data })); ++index; - indexInterval = intervals[index]; } // Remove any intervals that are completely overlapped by the input interval. - while (index < intervals.length && JulianDate.greaterThan(intervalStop, indexInterval.stop)) { + while (index < intervals.length && JulianDate.greaterThan(interval.stop, intervals[index].stop)) { result = true; intervals.splice(index, 1); - indexInterval = intervals[index]; } // Check for the case where the input interval ends on the same date // as an existing interval. - if (index < intervals.length && intervalStop.equals(indexInterval.stop)) { + if (index < intervals.length && interval.stop.equals(intervals[index].stop)) { result = true; - if (!intervalIsStopIncluded && indexInterval.isStopIncluded) { + if (!interval.isStopIncluded && intervals[index].isStopIncluded) { // Last point of interval should remain because the stop date is included in // the existing interval but is not included in the input interval. - if ((index + 1) < intervals.length && intervals[index + 1].start.equals(intervalStop) && indexInterval.data === intervals[index + 1].data) { + if (index + 1 < intervals.length && + intervals[index + 1].start.equals(interval.stop) && + intervals[index].data === intervals[index + 1].data) { // Combine single point with the next interval intervals.splice(index, 1); - indexInterval = new TimeInterval({ - start : indexInterval.start, - stop : indexInterval.stop, - isStartIncluded : true, - isStopIncluded : indexInterval.isStopIncluded, - data : indexInterval.data + intervals[index] = new TimeInterval({ + start: intervals[index].start, + stop: intervals[index].stop, + isStartIncluded: true, + isStopIncluded: intervals[index].isStopIncluded, + data: intervals[index].data }); } else { - indexInterval = new TimeInterval({ - start : intervalStop, - stop : intervalStop, - isStartIncluded : true, - isStopIncluded : true, - data : indexInterval.data + intervals[index] = new TimeInterval({ + start: interval.stop, + stop: interval.stop, + isStartIncluded: true, + isStopIncluded: true, + data: intervals[index].data }); } - intervals[index] = indexInterval; } else { // Interval is completely overlapped intervals.splice(index, 1); @@ -570,17 +573,16 @@ define([ // Truncate any partially-overlapped intervals. if (index < intervals.length && - (JulianDate.greaterThan(intervalStop, indexInterval.start) || - (intervalStop.equals(indexInterval.start) && - intervalIsStopIncluded && - indexInterval.isStartIncluded))) { + (JulianDate.greaterThan(interval.stop, intervals[index].start) || + (interval.stop.equals(intervals[index].start) && interval.isStopIncluded && intervals[index].isStartIncluded))) { + result = true; intervals[index] = new TimeInterval({ - start : intervalStop, - stop : indexInterval.stop, - isStartIncluded : !intervalIsStopIncluded, - isStopIncluded : indexInterval.isStopIncluded, - data : indexInterval.data + start: interval.stop, + stop: intervals[index].stop, + isStartIncluded: !interval.isStopIncluded, + isStopIncluded: intervals[index].isStopIncluded, + data: intervals[index].data }); } @@ -606,9 +608,9 @@ define([ } //>>includeEnd('debug'); + var result = new TimeIntervalCollection(); var left = 0; var right = 0; - var result = new TimeIntervalCollection(); var intervals = this._intervals; var otherIntervals = other._intervals; @@ -623,7 +625,7 @@ define([ // The following will return an intersection whose data is 'merged' if the callback is defined if (defined(mergeCallback) || ((defined(dataComparer) && dataComparer(leftInterval.data, rightInterval.data)) || - (!defined(dataComparer) && rightInterval.data === leftInterval.data))) { + (!defined(dataComparer) && rightInterval.data === leftInterval.data))) { var intersection = TimeInterval.intersect(leftInterval, rightInterval, new TimeInterval(), mergeCallback); if (!intersection.isEmpty) { @@ -634,9 +636,8 @@ define([ } if (JulianDate.lessThan(leftInterval.stop, rightInterval.stop) || - (leftInterval.stop.equals(rightInterval.stop) && - !leftInterval.isStopIncluded && - rightInterval.isStopIncluded)) { + (leftInterval.stop.equals(rightInterval.stop) && !leftInterval.isStopIncluded && rightInterval.isStopIncluded)) { + ++left; } else { ++right; diff --git a/Specs/Core/TimeIntervalCollectionSpec.js b/Specs/Core/TimeIntervalCollectionSpec.js index 268257742c03..671b82bf6502 100644 --- a/Specs/Core/TimeIntervalCollectionSpec.js +++ b/Specs/Core/TimeIntervalCollectionSpec.js @@ -3,13 +3,15 @@ defineSuite([ 'Core/defaultValue', 'Core/Iso8601', 'Core/JulianDate', - 'Core/TimeInterval' + 'Core/TimeInterval', + 'Core/TimeStandard' ], function( TimeIntervalCollection, defaultValue, Iso8601, JulianDate, - TimeInterval) { + TimeInterval, + TimeStandard) { 'use strict'; function defaultDataCallback(interval, index) { @@ -614,6 +616,438 @@ defineSuite([ expect(intervals.get(1).data.value).toEqual(3); }); + it('removeInterval works correctly', function() { + // test cases derived from STK Components test suite + + function createTimeInterval(startDays, stopDays, isStartIncluded, isStopIncluded) { + return new TimeInterval({ + start: new JulianDate(startDays, 0.0, TimeStandard.TAI), + stop: new JulianDate(stopDays, 0.0, TimeStandard.TAI), + isStartIncluded: isStartIncluded, + isStopIncluded: isStopIncluded + }); + } + + var intervals = new TimeIntervalCollection(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + + // Empty + expect(intervals.removeInterval(TimeInterval.EMPTY)).toEqual(false); + expect(intervals.length).toEqual(2); + + // Before first + expect(intervals.removeInterval(createTimeInterval(1.0, 5.0))).toEqual(false); + expect(intervals.length).toEqual(2); + + // After last + expect(intervals.removeInterval(createTimeInterval(50.0, 60.0))).toEqual(false); + expect(intervals.length).toEqual(2); + + // Inside hole + expect(intervals.removeInterval(createTimeInterval(22.0, 28.0))).toEqual(false); + expect(intervals.length).toEqual(2); + + // From beginning + expect(intervals.removeInterval(createTimeInterval(5.0, 15.0))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(0).start)).toEqual(15.0); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + + // From end + expect(intervals.removeInterval(createTimeInterval(35.0, 45.0))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(30.0); + expect(JulianDate.totalDays(intervals.get(1).stop)).toEqual(35.0); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + + // From middle of single interval + expect(intervals.removeInterval(createTimeInterval(12.0, 18.0))).toEqual(true); + expect(intervals.length).toEqual(3); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(12.0); + expect(intervals.get(0).isStopIncluded).toEqual(false); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(18.0); + expect(intervals.get(1).isStartIncluded).toEqual(false); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + intervals.addInterval(createTimeInterval(45.0, 50.0)); + + // Span an entire interval and into part of next + expect(intervals.removeInterval(createTimeInterval(25.0, 46.0))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(46.0); + expect(intervals.get(1).isStartIncluded).toEqual(false); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + intervals.addInterval(createTimeInterval(45.0, 50.0)); + + // Interval ends at same date as an existing interval + expect(intervals.removeInterval(createTimeInterval(25.0, 40.0))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(45.0); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + intervals.addInterval(createTimeInterval(45.0, 50.0)); + + // Interval ends at same date as an existing interval and single point of existing + // interval survives. + expect(intervals.removeInterval(createTimeInterval(25.0, 40.0, true, false))).toEqual(true); + expect(intervals.length).toEqual(3); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(40.0); + expect(JulianDate.totalDays(intervals.get(1).stop)).toEqual(40.0); + expect(intervals.get(1).isStartIncluded).toEqual(true); + expect(intervals.get(1).isStopIncluded).toEqual(true); + expect(JulianDate.totalDays(intervals.get(2).start)).toEqual(45.0); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + intervals.addInterval(createTimeInterval(30.0, 40.0)); + intervals.addInterval(createTimeInterval(40.0, 50.0, false, true)); + + // Interval ends at same date as an existing interval, single point of existing + // interval survives, and single point can be combined with the next interval. + expect(intervals.removeInterval(createTimeInterval(25.0, 40.0, true, false))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(40.0); + expect(intervals.get(1).isStartIncluded).toEqual(true); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + + // End point of removal interval overlaps first point of existing interval. + expect(intervals.removeInterval(createTimeInterval(0.0, 10.0))).toEqual(true); + expect(intervals.length).toEqual(1); + expect(JulianDate.totalDays(intervals.get(0).start)).toEqual(10.0); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + expect(intervals.get(0).isStartIncluded).toEqual(false); + expect(intervals.get(0).isStopIncluded).toEqual(true); + + intervals.removeAll(); + intervals.addInterval(createTimeInterval(10.0, 20.0)); + + // Start point of removal interval does NOT overlap last point of existing interval + // because the start point is not included. + expect(intervals.removeInterval(createTimeInterval(20.0, 30.0, false, true))).toEqual(false); + expect(intervals.length).toEqual(1); + expect(JulianDate.totalDays(intervals.get(0).start)).toEqual(10.0); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(20.0); + expect(intervals.get(0).isStartIncluded).toEqual(true); + expect(intervals.get(0).isStopIncluded).toEqual(true); + + // Removing an open interval from an otherwise identical closed interval + intervals.removeAll(); + intervals.addInterval(createTimeInterval(0.0, 20.0)); + expect(intervals.removeInterval(createTimeInterval(0.0, 20.0, false, false))).toEqual(true); + expect(intervals.length).toEqual(2); + expect(JulianDate.totalDays(intervals.get(0).start)).toEqual(0.0); + expect(JulianDate.totalDays(intervals.get(0).stop)).toEqual(0.0); + expect(intervals.get(0).isStartIncluded).toEqual(true); + expect(intervals.get(0).isStopIncluded).toEqual(true); + expect(JulianDate.totalDays(intervals.get(1).start)).toEqual(20.0); + expect(JulianDate.totalDays(intervals.get(1).stop)).toEqual(20.0); + expect(intervals.get(1).isStartIncluded).toEqual(true); + expect(intervals.get(1).isStopIncluded).toEqual(true); + }); + + it('removeInterval removes the first interval correctly', function() { + var intervals = new TimeIntervalCollection(); + var from1To3 = new TimeInterval({ + start: new JulianDate(1), + stop: new JulianDate(3), + isStopIncluded: true, + isStartIncluded: true, + data: '1-to-3' + }); + var from3To6 = new TimeInterval({ + start: new JulianDate(3), + stop: new JulianDate(6), + isStopIncluded: true, + isStartIncluded: true, + data: '3-to-6' + }); + + intervals.addInterval(from1To3); + intervals.addInterval(from3To6); + + expect(intervals.length).toEqual(2); + expect(intervals.get(0).isStartIncluded).toBeTruthy(); + expect(intervals.get(0).isStopIncluded).toBeFalsy(); // changed to false because 3-6 overlaps it + expect(intervals.get(0).start.dayNumber).toEqual(1); + expect(intervals.get(0).stop.dayNumber).toEqual(3); + expect(intervals.get(0).data).toEqual('1-to-3'); + expect(intervals.get(1).isStartIncluded).toBeTruthy(); + expect(intervals.get(1).isStopIncluded).toBeTruthy(); + expect(intervals.get(1).start.dayNumber).toEqual(3); + expect(intervals.get(1).stop.dayNumber).toEqual(6); + expect(intervals.get(1).data).toEqual('3-to-6'); + + var toRemove = new TimeInterval({ + start: new JulianDate(1), + stop: new JulianDate(3), + isStopIncluded: true, + isStartIncluded: true, + data: undefined + }); + + expect(intervals.removeInterval(toRemove)).toEqual(true); + expect(intervals.length).toEqual(1); + expect(intervals.start.dayNumber).toEqual(3); + expect(intervals.stop.dayNumber).toEqual(6); + expect(intervals.get(0).start.dayNumber).toEqual(3); + expect(intervals.get(0).stop.dayNumber).toEqual(6); + expect(intervals.get(0).isStartIncluded).toEqual(false); + expect(intervals.get(0).isStopIncluded).toEqual(true); + expect(intervals.get(0).data).toEqual('3-to-6'); + }); + + it('should add and remove intervals correctly (some kind of integration test)', function () { + // about the year 3000 + var CONST_DAY_NUM = 3000000; + + function intervalFromSeconds(seconds, data) { + // make all intervals a few seconds in length + return new TimeInterval({ + start: new JulianDate(CONST_DAY_NUM, seconds), + stop: new JulianDate(CONST_DAY_NUM, seconds + 4), + isStartIncluded: true, + isStopIncluded: true, + data: data + }); + } + + function addIntervals(collection, specs) { + specs.forEach(function(spec) { + collection.addInterval(intervalFromSeconds(spec.sec, spec.data)); + }); + } + + function removeInterval(collection, fromSecond, toSecond) { + collection.removeInterval(new TimeInterval({ + start: new JulianDate(CONST_DAY_NUM, fromSecond), + stop: new JulianDate(CONST_DAY_NUM, toSecond), + isStartIncluded: true, + isStopIncluded: true, + data: undefined + })); + } + + function expectCollection(collection, count, expectation) { + expectation.forEach(function(item) { + var interval = collection.findIntervalContainingDate(new JulianDate(CONST_DAY_NUM, item.sec)); + if (item.data === null) { + // expect the interval at this time not to exist + if (interval !== undefined) { + throw new Error('expected undefined at ' + item.sec + ' seconds but it was ' + interval.data); + } + expect(interval).toBeUndefined(); + } else if (interval === undefined) { + throw new Error('expected ' + item.data + ' at ' + item.sec + ' seconds, but it was undefined'); + } else if (interval.data !== item.data) { + throw new Error('expected ' + item.data + ' at ' + item.sec + ' seconds, but it was ' + interval.data); + } + }); + + if (collection.length !== count) { + throw new Error('Expected interval to have ' + count + ' elements but it had ' + collection.length); + } + } + + var collection = new TimeIntervalCollection(); + + addIntervals( + collection, + [ + { sec: 0, data: 0 }, + { sec: 2, data: 2 }, + { sec: 4, data: 4 }, + { sec: 6, data: 6 } + ] + ); + expectCollection( + collection, + 4, + [ + { sec: 0, data: 0 }, + { sec: 1, data: 0 }, + { sec: 2, data: 2 }, + { sec: 3, data: 2 }, + { sec: 4, data: 4 }, + { sec: 5, data: 4 }, + { sec: 6, data: 6 }, + { sec: 7, data: 6 }, + { sec: 8, data: 6 }, + { sec: 9, data: 6 }, + { sec: 10, data: 6 }, + { sec: 11, data: null } + ] + ); + + addIntervals( + collection, + [ + { sec: 1, data: 1 }, + { sec: 3, data: 3 } + ] + ); + expectCollection( + collection, + 4, + [ + { sec: 0, data: 0 }, + { sec: 1, data: 1 }, + { sec: 2, data: 1 }, + { sec: 3, data: 3 }, + { sec: 4, data: 3 }, + { sec: 5, data: 3 }, + { sec: 6, data: 3 }, + { sec: 7, data: 3 }, + { sec: 8, data: 6 }, + { sec: 9, data: 6 }, + { sec: 10, data: 6 }, + { sec: 11, data: null } + ] + ); + + addIntervals( + collection, + [ + { sec: 3, data: 31 } + ] + ); + expectCollection( + collection, + 4, + [ + { sec: 0, data: 0 }, + { sec: 1, data: 1 }, + { sec: 2, data: 1 }, + { sec: 3, data: 31 }, + { sec: 4, data: 31 }, + { sec: 5, data: 31 }, + { sec: 6, data: 31 }, + { sec: 7, data: 31 }, + { sec: 8, data: 6 }, + { sec: 9, data: 6 }, + { sec: 10, data: 6 }, + { sec: 11, data: null } + ] + ); + + removeInterval(collection, 3, 8); + expectCollection( + collection, + 3, + [ + { sec: 0, data: 0 }, + { sec: 1, data: 1 }, + { sec: 2, data: 1 }, + { sec: 3, data: null }, + { sec: 4, data: null }, + { sec: 5, data: null }, + { sec: 6, data: null }, + { sec: 7, data: null }, + { sec: 8, data: null }, + { sec: 9, data: 6 }, + { sec: 10, data: 6 }, + { sec: 11, data: null } + ] + ); + + removeInterval(collection, 0, 1); + expectCollection( + collection, + 2, + [ + { sec: 0, data: null }, + { sec: 1, data: null }, + { sec: 2, data: 1 }, + { sec: 3, data: null }, + { sec: 4, data: null }, + { sec: 5, data: null }, + { sec: 6, data: null }, + { sec: 7, data: null }, + { sec: 8, data: null }, + { sec: 9, data: 6 }, + { sec: 10, data: 6 }, + { sec: 11, data: null } + ] + ); + + removeInterval(collection, 0, 11); + expectCollection( + collection, + 0, + [ + { sec: 0, data: null }, + { sec: 11, data: null } + ] + ); + + addIntervals( + collection, + [ + { sec: 1, data: 1 }, + { sec: 12, data: 12 } + ] + ); + expectCollection( + collection, + 2, + [ + { sec: 0, data: null }, + { sec: 1, data: 1 }, + { sec: 2, data: 1 }, + { sec: 3, data: 1 }, + { sec: 4, data: 1 }, + { sec: 5, data: 1 }, + { sec: 6, data: null }, + { sec: 7, data: null }, + { sec: 8, data: null }, + { sec: 9, data: null }, + { sec: 10, data: null }, + { sec: 11, data: null }, + { sec: 12, data: 12 }, + { sec: 13, data: 12 }, + { sec: 14, data: 12 }, + { sec: 15, data: 12 }, + { sec: 16, data: 12 }, + { sec: 17, data: null } + ] + ); + + removeInterval(collection, 0, 3); + expectCollection( + collection, + 2, + [ + { sec: 0, data: null }, + { sec: 1, data: null }, + { sec: 2, data: null }, + { sec: 3, data: null }, + { sec: 4, data: 1 }, + { sec: 5, data: 1 }, + { sec: 6, data: null }, + { sec: 7, data: null }, + { sec: 8, data: null }, + { sec: 12, data: 12 }, + { sec: 16, data: 12 }, + { sec: 17, data: null } + ] + ); + }); + it('removeInterval leaves a hole', function() { var intervals = new TimeIntervalCollection(); var interval = new TimeInterval({ @@ -643,7 +1077,7 @@ defineSuite([ expect(intervals.get(1).isStopIncluded).toEqual(true); }); - it('removeInterval with an interval of the exact same size works..', function() { + it('removeInterval with an interval of the exact same size works.', function() { var intervals = new TimeIntervalCollection(); var interval = new TimeInterval({ start : new JulianDate(1),