Skip to content

Commit 5476619

Browse files
committed
frame changed event, ready promise, and other updates
1 parent adf8f4e commit 5476619

9 files changed

+124
-31
lines changed

Apps/Sandcastle/gallery/Time Dynamic Point Cloud.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<meta charset="utf-8">
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
7-
<meta name="description" content="Time Dynamic Point Cloud">
8-
<meta name="cesium-sandcastle-labels" content="Showcases, 3D Tiles">
7+
<meta name="description" content="Render a time dynamic point cloud from a set of Point Cloud tiles and timestamps.">
8+
<meta name="cesium-sandcastle-labels" content="Showcases">
99
<title>Cesium Demo</title>
1010
<script type="text/javascript" src="../Sandcastle-header.js"></script>
1111
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
Loading

Source/Scene/Cesium3DTileset.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ define([
417417
* <p>
418418
* If there are no event listeners, error messages will be logged to the console.
419419
* </p>
420+
* <p>
421+
* The error object passed to the listener contains two properties:
422+
* <ul>
423+
* <li><code>url</code>: the url of the failed tile.</li>
424+
* <li><code>message</code>: the error message.</li>
425+
* </ul>
420426
*
421427
* @type {Event}
422428
* @default new Event()
@@ -1839,14 +1845,6 @@ define([
18391845

18401846
///////////////////////////////////////////////////////////////////////////
18411847

1842-
/**
1843-
* Called when {@link Viewer} or {@link CesiumWidget} render the scene to
1844-
* get the draw commands needed to render this primitive.
1845-
* <p>
1846-
* Do not call this function directly. This is documented just to
1847-
* list the exceptions that may be propagated when the scene is rendered:
1848-
* </p>
1849-
*/
18501848
Cesium3DTileset.prototype.update = function(frameState) {
18511849
if (frameState.mode === SceneMode.MORPHING) {
18521850
return;

Source/Scene/PointCloud.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -475,16 +475,31 @@ define([
475475
var scratchMin = new Cartesian3();
476476
var scratchMax = new Cartesian3();
477477
var scratchPosition = new Cartesian3();
478+
var randomValues;
479+
480+
function getRandomValues(samplesLength) {
481+
// Use same random values across all runs
482+
if (!defined(randomValues)) {
483+
CesiumMath.setRandomNumberSeed(0);
484+
randomValues = new Array(20);
485+
for (var i = 0; i < samplesLength; ++i) {
486+
randomValues[i] = CesiumMath.nextRandomNumber();
487+
}
488+
}
489+
return randomValues;
490+
}
478491

479492
function computeApproximateBoundingSphereFromPositions(positions) {
493+
var maximumSamplesLength = 20;
480494
var pointsLength = positions.length / 3;
481-
var samplesLength = Math.min(pointsLength, 20);
495+
var samplesLength = Math.min(pointsLength, maximumSamplesLength);
496+
var randomValues = getRandomValues(maximumSamplesLength);
482497
var maxValue = Number.MAX_VALUE;
483498
var minValue = -Number.MAX_VALUE;
484499
var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin);
485500
var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax);
486501
for (var i = 0; i < samplesLength; ++i) {
487-
var index = Math.floor(i * pointsLength / samplesLength);
502+
var index = Math.floor(randomValues[i] * pointsLength);
488503
var position = Cartesian3.unpack(positions, index * 3, scratchPosition);
489504
Cartesian3.minimumByComponent(min, position, min);
490505
Cartesian3.maximumByComponent(max, position, max);

Source/Scene/TimeDynamicImagery.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ define([
2525
* @constructor
2626
*
2727
* @param {Object} options Object with the following properties:
28-
* @param {Clock} options.clock A Clock instance that is used when determining the value for the time dimension. Required when options.times is specified.
29-
* @param {TimeIntervalCollection} options.times TimeIntervalCollection with its data property being an object containing time dynamic dimension and their values.
28+
* @param {Clock} options.clock A Clock instance that is used when determining the value for the time dimension. Required when <code>options.times</code> is specified.
29+
* @param {TimeIntervalCollection} options.times TimeIntervalCollection with its <code>data</code> property being an object containing time dynamic dimension and their values.
3030
* @param {Function} options.requestImageFunction A function that will request imagery tiles.
3131
* @param {Function} options.reloadFunction A function that will be called when all imagery tiles need to be reloaded.
3232
*/

Source/Scene/TimeDynamicPointCloud.js

+55-10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ define([
1212
'../Core/Math',
1313
'../Core/Matrix4',
1414
'../Core/Resource',
15+
'../ThirdParty/when',
1516
'./ClippingPlaneCollection',
1617
'./PointCloud',
1718
'./PointCloudEyeDomeLighting',
@@ -32,6 +33,7 @@ define([
3233
CesiumMath,
3334
Matrix4,
3435
Resource,
36+
when,
3537
ClippingPlaneCollection,
3638
PointCloud,
3739
PointCloudEyeDomeLighting,
@@ -155,18 +157,39 @@ define([
155157
* <p>
156158
* If there are no event listeners, error messages will be logged to the console.
157159
* </p>
160+
* <p>
161+
* The error object passed to the listener contains two properties:
162+
* <ul>
163+
* <li><code>uri</code>: the uri of the failed frame.</li>
164+
* <li><code>message</code>: the error message.</li>
165+
* </ul>
158166
*
159167
* @type {Event}
160168
* @default new Event()
161169
*
162170
* @example
163171
* pointCloud.frameFailed.addEventListener(function(error) {
164-
* console.log('An error occurred loading frame: ' + error.url);
172+
* console.log('An error occurred loading frame: ' + error.uri);
165173
* console.log('Error: ' + error.message);
166174
* });
167175
*/
168176
this.frameFailed = new Event();
169177

178+
/**
179+
* The event fired to indicate that a new frame was rendered.
180+
* <p>
181+
* The time dynamic point cloud {@link TimeDynamicPointCloud} is passed to the event listener.
182+
* </p>
183+
* @type {Event}
184+
* @default new Event()
185+
*
186+
* @example
187+
* pointCloud.frameChanged.addEventListener(function(timeDynamicPointCloud) {
188+
* viewer.camera.viewBoundingSphere(timeDynamicPointCloud.boundingSphere);
189+
* });
190+
*/
191+
this.frameChanged = new Event();
192+
170193
this._clock = options.clock;
171194
this._intervals = options.intervals;
172195
this._clippingPlanes = undefined;
@@ -182,6 +205,7 @@ define([
182205
this._nextInterval = undefined;
183206
this._lastRenderedFrame = undefined;
184207
this._clockMultiplier = 0.0;
208+
this._readyPromise = when.defer();
185209

186210
// For calculating average load time of the last N frames
187211
this._runningSum = 0.0;
@@ -238,6 +262,20 @@ define([
238262
return this._lastRenderedFrame.pointCloud.boundingSphere;
239263
}
240264
}
265+
},
266+
267+
/**
268+
* Gets the promise that will be resolved when the point cloud renders a frame for the first time.
269+
*
270+
* @memberof TimeDynamicPointCloud.prototype
271+
*
272+
* @type {Promise.<TimeDynamicPointCloud>}
273+
* @readonly
274+
*/
275+
readyPromise : {
276+
get : function() {
277+
return this._readyPromise.promise;
278+
}
241279
}
242280
});
243281

@@ -345,7 +383,7 @@ define([
345383
var message = defined(error.message) ? error.message : error.toString();
346384
if (that.frameFailed.numberOfListeners > 0) {
347385
that.frameFailed.raiseEvent({
348-
url : uri,
386+
uri : uri,
349387
message : message
350388
});
351389
} else {
@@ -576,14 +614,6 @@ define([
576614
clippingPlanesDirty : false
577615
};
578616

579-
/**
580-
* Called when {@link Viewer} or {@link CesiumWidget} render the scene to
581-
* get the draw commands needed to render this primitive.
582-
* <p>
583-
* Do not call this function directly. This is documented just to
584-
* list the exceptions that may be propagated when the scene is rendered:
585-
* </p>
586-
*/
587617
TimeDynamicPointCloud.prototype.update = function(frameState) {
588618
if (frameState.mode === SceneMode.MORPHING) {
589619
return;
@@ -681,6 +711,21 @@ define([
681711
loadFrame(this, nextInterval, updateState, frameState);
682712
}
683713

714+
var that = this;
715+
if (defined(frame) && !defined(this._lastRenderedFrame)) {
716+
frameState.afterRender.push(function() {
717+
that._readyPromise.resolve(that);
718+
});
719+
}
720+
721+
if (defined(frame) && (frame !== this._lastRenderedFrame)) {
722+
if (that.frameChanged.numberOfListeners > 0) {
723+
frameState.afterRender.push(function() {
724+
that.frameChanged.raiseEvent(that);
725+
});
726+
}
727+
}
728+
684729
this._previousInterval = previousInterval;
685730
this._nextInterval = nextInterval;
686731
this._lastRenderedFrame = frame;

Source/Scene/WebMapTileServiceImageryProvider.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ define([
5353
* @param {String} options.tileMatrixSetID The identifier of the TileMatrixSet to use for WMTS requests.
5454
* @param {Array} [options.tileMatrixLabels] A list of identifiers in the TileMatrix to use for WMTS requests, one per TileMatrix level.
5555
* @param {Clock} [options.clock] A Clock instance that is used when determining the value for the time dimension. Required when options.times is specified.
56-
* @param {TimeIntervalCollection} [options.times] TimeIntervalCollection with its data property being an object containing time dynamic dimension and their values.
56+
* @param {TimeIntervalCollection} [options.times] TimeIntervalCollection with its <code>data</code> property being an object containing time dynamic dimension and their values.
5757
* @param {Object} [options.dimensions] A object containing static dimensions and their values.
5858
* @param {Number} [options.tileWidth=256] The tile width in pixels.
5959
* @param {Number} [options.tileHeight=256] The tile height in pixels.

Source/Widgets/Viewer/Viewer.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1938,9 +1938,9 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
19381938

19391939
// If zoomTarget was TimeDynamicPointCloud
19401940
if (target instanceof TimeDynamicPointCloud) {
1941-
boundingSphere = target.boundingSphere;
1942-
if (defined(boundingSphere)) {
1941+
return target.readyPromise.then(function() {
19431942
// If offset was originally undefined then give it base value instead of empty object
1943+
var boundingSphere = target.boundingSphere;
19441944
if (!defined(zoomOptions.offset)) {
19451945
zoomOptions.offset = new HeadingPitchRange(0.0, -0.5, boundingSphere.radius);
19461946
}
@@ -1968,8 +1968,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
19681968
}
19691969

19701970
clearZoom(viewer);
1971-
}
1972-
return;
1971+
});
19731972
}
19741973

19751974
// If zoomTarget was an ImageryLayer

Specs/Scene/TimeDynamicPointCloudSpec.js

+38-2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,15 @@ defineSuite([
258258
});
259259
});
260260

261+
it('resolves ready promise', function() {
262+
var pointCloud = createTimeDynamicPointCloud();
263+
return loadFrame(pointCloud).then(function() {
264+
return pointCloud.readyPromise.then(function(pointCloud) {
265+
expect(pointCloud.boundingSphere).toBeDefined();
266+
});
267+
});
268+
});
269+
261270
it('sets show', function() {
262271
var pointCloud = createTimeDynamicPointCloud();
263272

@@ -705,7 +714,7 @@ defineSuite([
705714
for (i = 0; i < 5; ++i) {
706715
var arg = spyUpdate.calls.argsFor(i)[0];
707716
expect(arg).toBeDefined();
708-
expect(arg.url).toContain(i + '.pnts');
717+
expect(arg.uri).toContain(i + '.pnts');
709718
expect(arg.message).toBe('404');
710719
}
711720
});
@@ -735,12 +744,39 @@ defineSuite([
735744
}).then(function() {
736745
var arg = spyUpdate.calls.argsFor(0)[0];
737746
expect(arg).toBeDefined();
738-
expect(arg.url).toContain('1.pnts');
747+
expect(arg.uri).toContain('1.pnts');
739748
expect(arg.message).toBe('my error');
740749
});
741750
});
742751
});
743752

753+
it('raises frame changed event', function() {
754+
var pointCloud = createTimeDynamicPointCloud();
755+
var spyFrameChanged = jasmine.createSpy('listener');
756+
pointCloud.frameChanged.addEventListener(spyFrameChanged);
757+
758+
return loadAllFrames(pointCloud).then(function() {
759+
expect(spyFrameChanged.calls.count()).toBe(5);
760+
761+
// Go to random frame
762+
goToFrame(2);
763+
scene.renderForSpecs();
764+
expect(spyFrameChanged.calls.count()).toBe(6);
765+
766+
// Go out of range. No event raised.
767+
clock.currentTime = JulianDate.addSeconds(dates[0], -10.0, new JulianDate());
768+
scene.renderForSpecs();
769+
expect(spyFrameChanged.calls.count()).toBe(6);
770+
771+
goToFrame(0);
772+
scene.renderForSpecs();
773+
expect(spyFrameChanged.calls.count()).toBe(7);
774+
775+
expect(spyFrameChanged.calls.argsFor(0)[0]).toBe(pointCloud);
776+
});
777+
778+
});
779+
744780
it('destroys', function() {
745781
var pointCloud = createTimeDynamicPointCloud();
746782
return loadAllFrames(pointCloud).then(function() {

0 commit comments

Comments
 (0)