diff --git a/Apps/Sandcastle/gallery/Time Dynamic Point Cloud.html b/Apps/Sandcastle/gallery/Time Dynamic Point Cloud.html
index 64a4de65e926..eed1f9625dd4 100644
--- a/Apps/Sandcastle/gallery/Time Dynamic Point Cloud.html
+++ b/Apps/Sandcastle/gallery/Time Dynamic Point Cloud.html
@@ -81,11 +81,7 @@
clock.stopTime = stop;
clock.clockRange = Cesium.ClockRange.LOOP_STOP;
-viewer.camera.setView({
- destination: new Cesium.Cartesian3(1215034.013185971, -4736376.364704681, 4081587.528471664),
- orientation: new Cesium.HeadingPitchRoll(6.2077134961933265, -0.6084278203800215, 6.282880789189662),
- endTransform : Cesium.Matrix4.IDENTITY
-});
+viewer.zoomTo(pointCloud, new Cesium.HeadingPitchRange(0.0, -0.5, 50.0));
//Sandcastle_End
Sandcastle.finishedLoading();
}
diff --git a/Source/Scene/PointCloud.js b/Source/Scene/PointCloud.js
index b9049515f892..53bb32d7fe01 100644
--- a/Source/Scene/PointCloud.js
+++ b/Source/Scene/PointCloud.js
@@ -180,7 +180,7 @@ define([
this.time = 0.0; // For styling
this.shadows = ShadowMode.ENABLED;
- this.boundingSphere = undefined;
+ this._boundingSphere = undefined;
this.clippingPlanes = undefined;
this.isClipped = false;
@@ -229,6 +229,17 @@ define([
set : function(value) {
this._highlightColor = Color.clone(value, this._highlightColor);
}
+ },
+
+ boundingSphere : {
+ get : function() {
+ if (defined(this._drawCommand)) {
+ return this._drawCommand.boundingVolume;
+ }
+ },
+ set : function(value) {
+ this._boundingSphere = BoundingSphere.clone(value);
+ }
}
});
@@ -634,9 +645,9 @@ define([
if (pointCloud._cull) {
if (isQuantized || isQuantizedDraco) {
- pointCloud.boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.ZERO, pointCloud._quantizedVolumeScale);
+ pointCloud._boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.ZERO, pointCloud._quantizedVolumeScale);
} else {
- pointCloud.boundingSphere = computeApproximateBoundingSphereFromPositions(positions);
+ pointCloud._boundingSphere = computeApproximateBoundingSphereFromPositions(positions);
}
}
@@ -1298,7 +1309,7 @@ define([
}
var boundingSphere = this._drawCommand.boundingVolume;
- BoundingSphere.clone(this.boundingSphere, boundingSphere);
+ BoundingSphere.clone(this._boundingSphere, boundingSphere);
if (this._cull) {
var center = boundingSphere.center;
diff --git a/Source/Scene/TimeDynamicPointCloud.js b/Source/Scene/TimeDynamicPointCloud.js
index 6022db402b15..7e2bc7d7df10 100644
--- a/Source/Scene/TimeDynamicPointCloud.js
+++ b/Source/Scene/TimeDynamicPointCloud.js
@@ -222,6 +222,22 @@ define([
get : function() {
return this._totalMemoryUsageInBytes;
}
+ },
+
+ /**
+ * The bounding sphere of the frame being rendered. Returns undefined
if no frame is being rendered.
+ *
+ * @memberof TimeDynamicPointCloud.prototype
+ *
+ * @type {BoundingSphere}
+ * @readonly
+ */
+ boundingSphere : {
+ get : function() {
+ if (defined(this._lastRenderedFrame)) {
+ return this._lastRenderedFrame.pointCloud.boundingSphere;
+ }
+ }
}
});
diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js
index 2c85c9fc522e..a33216b3cc97 100644
--- a/Source/Widgets/Viewer/Viewer.js
+++ b/Source/Widgets/Viewer/Viewer.js
@@ -24,6 +24,7 @@ define([
'../../Scene/Cesium3DTileset',
'../../Scene/ImageryLayer',
'../../Scene/SceneMode',
+ '../../Scene/TimeDynamicPointCloud',
'../../ThirdParty/knockout',
'../../ThirdParty/when',
'../Animation/Animation',
@@ -71,6 +72,7 @@ define([
Cesium3DTileset,
ImageryLayer,
SceneMode,
+ TimeDynamicPointCloud,
knockout,
when,
Animation,
@@ -1740,7 +1742,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
* target will be the range. The heading will be determined from the offset. If the heading cannot be
* determined from the offset, the heading will be north.
*
- * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|Promise.} target The entity, array of entities, entity collection, data source, Cesium#DTileset, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
+ * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise.} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
* @param {HeadingPitchRange} [offset] The offset from the center of the entity in the local east-north-up reference frame.
* @returns {Promise.} A Promise that resolves to true if the zoom was successful or false if the target is not currently visualized in the scene or the zoom was cancelled.
*/
@@ -1766,7 +1768,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
* target will be the range. The heading will be determined from the offset. If the heading cannot be
* determined from the offset, the heading will be north.
*
- * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|Promise.} target The entity, array of entities, entity collection, data source, Cesium3DTileset, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
+ * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise.} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
* @param {Object} [options] Object with the following properties:
* @param {Number} [options.duration=3.0] The duration of the flight in seconds.
* @param {Number} [options.maximumHeight] The maximum height at the peak of the flight.
@@ -1818,6 +1820,12 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
return;
}
+ //If the zoom target is a TimeDynamicPointCloud
+ if (zoomTarget instanceof TimeDynamicPointCloud) {
+ that._zoomTarget = zoomTarget;
+ return;
+ }
+
//If the zoom target is a data source, and it's in the middle of loading, wait for it to finish loading.
if (zoomTarget.isLoading && defined(zoomTarget.loadingEvent)) {
var removeEvent = zoomTarget.loadingEvent.addEventListener(function() {
@@ -1891,12 +1899,13 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
var zoomPromise = viewer._zoomPromise;
var zoomOptions = defaultValue(viewer._zoomOptions, {});
var options;
+ var boundingSphere;
// If zoomTarget was Cesium3DTileset
if (target instanceof Cesium3DTileset) {
return target.readyPromise.then(function() {
var boundingSphere = target.boundingSphere;
- // if offset was originally undefined then give it base value instead of empty object
+ // If offset was originally undefined then give it base value instead of empty object
if (!defined(zoomOptions.offset)) {
zoomOptions.offset = new HeadingPitchRange(0.0, -0.5, boundingSphere.radius);
}
@@ -1919,7 +1928,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
camera.viewBoundingSphere(boundingSphere, zoomOptions.offset);
camera.lookAtTransform(Matrix4.IDENTITY);
- // finish the promise
+ // Finish the promise
zoomPromise.resolve(true);
}
@@ -1927,7 +1936,43 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
});
}
- //If zoomTarget was an ImageryLayer
+ // If zoomTarget was TimeDynamicPointCloud
+ if (target instanceof TimeDynamicPointCloud) {
+ boundingSphere = target.boundingSphere;
+ if (defined(boundingSphere)) {
+ // If offset was originally undefined then give it base value instead of empty object
+ if (!defined(zoomOptions.offset)) {
+ zoomOptions.offset = new HeadingPitchRange(0.0, -0.5, boundingSphere.radius);
+ }
+
+ options = {
+ offset : zoomOptions.offset,
+ duration : zoomOptions.duration,
+ maximumHeight : zoomOptions.maximumHeight,
+ complete : function() {
+ zoomPromise.resolve(true);
+ },
+ cancel : function() {
+ zoomPromise.resolve(false);
+ }
+ };
+
+ if (viewer._zoomIsFlight) {
+ camera.flyToBoundingSphere(boundingSphere, options);
+ } else {
+ camera.viewBoundingSphere(boundingSphere, zoomOptions.offset);
+ camera.lookAtTransform(Matrix4.IDENTITY);
+
+ // Finish the promise
+ zoomPromise.resolve(true);
+ }
+
+ clearZoom(viewer);
+ }
+ return;
+ }
+
+ // If zoomTarget was an ImageryLayer
if (target instanceof Rectangle) {
options = {
destination : target,
@@ -1972,7 +2017,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
//Stop tracking the current entity.
viewer.trackedEntity = undefined;
- var boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres);
+ boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres);
if (!viewer._zoomIsFlight) {
camera.viewBoundingSphere(boundingSphere, viewer._zoomOptions.offset);
diff --git a/Specs/Scene/TimeDynamicPointCloudSpec.js b/Specs/Scene/TimeDynamicPointCloudSpec.js
index 1fb11177df9a..479198de4056 100644
--- a/Specs/Scene/TimeDynamicPointCloudSpec.js
+++ b/Specs/Scene/TimeDynamicPointCloudSpec.js
@@ -1,5 +1,6 @@
defineSuite([
'Scene/TimeDynamicPointCloud',
+ 'Core/BoundingSphere',
'Core/Cartesian3',
'Core/Clock',
'Core/ClockStep',
@@ -26,6 +27,7 @@ defineSuite([
'ThirdParty/when'
], function(
TimeDynamicPointCloud,
+ BoundingSphere,
Cartesian3,
Clock,
ClockStep,
@@ -240,6 +242,22 @@ defineSuite([
});
});
+ it('gets bounding sphere of the rendered frame', function() {
+ var pointCloud = createTimeDynamicPointCloud({
+ useTransforms : true
+ });
+ expect(pointCloud.boundingSphere).toBeUndefined(); // Undefined until a frame is rendered
+ return loadAllFrames(pointCloud).then(function() {
+ var boundingSphereFrame0 = pointCloud.boundingSphere;
+ expect(boundingSphereFrame0).toBeDefined();
+ goToFrame(1);
+ scene.renderForSpecs();
+ var boundingSphereFrame1 = pointCloud.boundingSphere;
+ expect(boundingSphereFrame1).toBeDefined();
+ expect(BoundingSphere.equals(boundingSphereFrame0, boundingSphereFrame1)).toBe(false);
+ });
+ });
+
it('sets show', function() {
var pointCloud = createTimeDynamicPointCloud();
diff --git a/Specs/Widgets/Viewer/ViewerSpec.js b/Specs/Widgets/Viewer/ViewerSpec.js
index e26ad717d42e..2fd86c1ad760 100644
--- a/Specs/Widgets/Viewer/ViewerSpec.js
+++ b/Specs/Widgets/Viewer/ViewerSpec.js
@@ -5,10 +5,12 @@ defineSuite([
'Core/ClockRange',
'Core/ClockStep',
'Core/Color',
+ 'Core/defined',
'Core/EllipsoidTerrainProvider',
'Core/HeadingPitchRange',
'Core/JulianDate',
'Core/Matrix4',
+ 'Core/TimeIntervalCollection',
'Core/WebMercatorProjection',
'DataSources/BoundingSphereState',
'DataSources/ConstantPositionProperty',
@@ -23,6 +25,7 @@ defineSuite([
'Scene/ImageryLayerCollection',
'Scene/SceneMode',
'Scene/ShadowMode',
+ 'Scene/TimeDynamicPointCloud',
'Specs/createViewer',
'Specs/DomEventSimulator',
'Specs/MockDataSource',
@@ -46,10 +49,12 @@ defineSuite([
ClockRange,
ClockStep,
Color,
+ defined,
EllipsoidTerrainProvider,
HeadingPitchRange,
JulianDate,
Matrix4,
+ TimeIntervalCollection,
WebMercatorProjection,
BoundingSphereState,
ConstantPositionProperty,
@@ -64,6 +69,7 @@ defineSuite([
ImageryLayerCollection,
SceneMode,
ShadowMode,
+ TimeDynamicPointCloud,
createViewer,
DomEventSimulator,
MockDataSource,
@@ -1108,6 +1114,92 @@ defineSuite([
});
});
+ function loadTimeDynamicPointCloud(viewer) {
+ var scene = viewer.scene;
+ var clock = viewer.clock;
+
+ var uri = './Data/Cesium3DTiles/PointCloud/PointCloudTimeDynamic/0.pnts';
+ var dates = [
+ '2018-07-19T15:18:00Z',
+ '2018-07-19T15:18:00.5Z'
+ ];
+
+ function dataCallback() {
+ return {
+ uri: uri
+ };
+ }
+
+ var timeIntervalCollection = TimeIntervalCollection.fromIso8601DateArray({
+ iso8601Dates: dates,
+ dataCallback: dataCallback
+ });
+
+ var pointCloud = new TimeDynamicPointCloud({
+ intervals : timeIntervalCollection,
+ clock : clock
+ });
+ viewer.scene.primitives.add(pointCloud);
+
+ var start = JulianDate.fromIso8601(dates[0]);
+
+ clock.startTime = start;
+ clock.currentTime = start;
+ clock.multiplier = 0.0;
+
+ return pollToPromise(function() {
+ pointCloud.update(scene.frameState);
+ var frame = pointCloud._frames[0];
+ return defined(frame) && frame.ready;
+ }).then(function() {
+ return pointCloud;
+ });
+ }
+
+ it('zoomTo zooms to TimeDynamicPointCloud with default offset when offset not defined', function() {
+ viewer = createViewer(container);
+ return loadTimeDynamicPointCloud(viewer).then(function(pointCloud) {
+ var expectedBoundingSphere = pointCloud.boundingSphere;
+ var expectedOffset = new HeadingPitchRange(0.0, -0.5, expectedBoundingSphere.radius);
+
+ var promise = viewer.zoomTo(pointCloud);
+ var wasCompleted = false;
+ spyOn(viewer.camera, 'viewBoundingSphere').and.callFake(function(boundingSphere, offset) {
+ expect(boundingSphere).toEqual(expectedBoundingSphere);
+ expect(offset).toEqual(expectedOffset);
+ wasCompleted = true;
+ });
+
+ viewer._postRender();
+
+ return promise.then(function() {
+ expect(wasCompleted).toEqual(true);
+ });
+ });
+ });
+
+ it('zoomTo zooms to TimeDynamicPointCloud with offset', function() {
+ viewer = createViewer(container);
+ return loadTimeDynamicPointCloud(viewer).then(function(pointCloud) {
+ var expectedBoundingSphere = pointCloud.boundingSphere;
+ var expectedOffset = new HeadingPitchRange(0.4, 1.2, 4.0 * expectedBoundingSphere.radius);
+
+ var promise = viewer.zoomTo(pointCloud, expectedOffset);
+ var wasCompleted = false;
+ spyOn(viewer.camera, 'viewBoundingSphere').and.callFake(function(boundingSphere, offset) {
+ expect(boundingSphere).toEqual(expectedBoundingSphere);
+ expect(offset).toEqual(expectedOffset);
+ wasCompleted = true;
+ });
+
+ viewer._postRender();
+
+ return promise.then(function() {
+ expect(wasCompleted).toEqual(true);
+ });
+ });
+ });
+
it('zoomTo zooms to entity with undefined offset when offset not defined', function() {
viewer = createViewer(container);
viewer.entities.add({
@@ -1208,7 +1300,6 @@ defineSuite([
expect(wasCompleted).toEqual(true);
});
});
-
});
it('flyTo flies to Cesium3DTileset with default offset when offset not defined', function() {
@@ -1239,11 +1330,10 @@ defineSuite([
return promise.then(function() {
expect(wasCompleted).toEqual(true);
});
-
});
});
- it('flyTo flies to target when target is Cesium3DTileset and options are defined', function() {
+ it('flyTo flies to Cesium3DTileset when options are defined', function() {
viewer = createViewer(container);
var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json';
@@ -1275,7 +1365,78 @@ defineSuite([
return promise.then(function() {
expect(wasCompleted).toEqual(true);
});
+ });
+ });
+
+ it('flyTo flies to TimeDynamicPointCloud with default offset when options not defined', function() {
+ viewer = createViewer(container);
+ return loadTimeDynamicPointCloud(viewer).then(function(pointCloud) {
+ var promise = viewer.flyTo(pointCloud);
+ var wasCompleted = false;
+ spyOn(viewer.camera, 'flyToBoundingSphere').and.callFake(function(target, options) {
+ expect(options.offset).toBeDefined();
+ expect(options.duration).toBeUndefined();
+ expect(options.maximumHeight).toBeUndefined();
+ wasCompleted = true;
+ options.complete();
+ });
+
+ viewer._postRender();
+
+ return promise.then(function() {
+ expect(wasCompleted).toEqual(true);
+ });
+ });
+ });
+
+ it('flyTo flies to TimeDynamicPointCloud with default offset when offset not defined', function() {
+ viewer = createViewer(container);
+ return loadTimeDynamicPointCloud(viewer).then(function(pointCloud) {
+ var options = {};
+ var promise = viewer.flyTo(pointCloud, options);
+ var wasCompleted = false;
+
+ spyOn(viewer.camera, 'flyToBoundingSphere').and.callFake(function(target, options) {
+ expect(options.offset).toBeDefined();
+ expect(options.duration).toBeUndefined();
+ expect(options.maximumHeight).toBeUndefined();
+ wasCompleted = true;
+ options.complete();
+ });
+
+ viewer._postRender();
+
+ return promise.then(function() {
+ expect(wasCompleted).toEqual(true);
+ });
+ });
+ });
+
+ it('flyTo flies to TimeDynamicPointCloud when options are defined', function() {
+ viewer = createViewer(container);
+ return loadTimeDynamicPointCloud(viewer).then(function(pointCloud) {
+ var offsetVal = new HeadingPitchRange(3.0, 0.2, 2.3);
+ var options = {
+ offset : offsetVal,
+ duration : 3.0,
+ maximumHeight : 5.0
+ };
+ var promise = viewer.flyTo(pointCloud, options);
+ var wasCompleted = false;
+
+ spyOn(viewer.camera, 'flyToBoundingSphere').and.callFake(function(target, options) {
+ expect(options.duration).toBeDefined();
+ expect(options.maximumHeight).toBeDefined();
+ wasCompleted = true;
+ options.complete();
+ });
+
+ viewer._postRender();
+
+ return promise.then(function() {
+ expect(wasCompleted).toEqual(true);
+ });
});
});
@@ -1309,7 +1470,6 @@ defineSuite([
return promise.then(function() {
expect(wasCompleted).toEqual(true);
});
-
});
it('flyTo flys to entity with default offset when offset not defined', function() {