/*global define*/
define([
'../../Core/Color',
'../../Core/defined',
'../../Core/defineProperties',
'../../Core/destroyObject',
'../../Core/DeveloperError',
'../../Core/Rectangle',
'../../Core/ScreenSpaceEventHandler',
'../../Core/ScreenSpaceEventType',
'../../Scene/DebugModelMatrixPrimitive',
'../../Scene/PerformanceDisplay',
'../../Scene/TileCoordinatesImageryProvider',
'../../ThirdParty/knockout',
'../createCommand'
], function(
Color,
defined,
defineProperties,
destroyObject,
DeveloperError,
Rectangle,
ScreenSpaceEventHandler,
ScreenSpaceEventType,
DebugModelMatrixPrimitive,
PerformanceDisplay,
TileCoordinatesImageryProvider,
knockout,
createCommand) {
'use strict';
function frustumStatsToString(stats) {
var str;
if (defined(stats)) {
str = 'Command Statistics';
var com = stats.commandsInFrustums;
for (var n in com) {
if (com.hasOwnProperty(n)) {
var num = parseInt(n, 10);
var s;
if (num === 7) {
s = '1, 2 and 3';
} else {
var f = [];
for (var i = 2; i >= 0; i--) {
var p = Math.pow(2, i);
if (num >= p) {
f.push(i + 1);
num -= p;
}
}
s = f.reverse().join(' and ');
}
str += '<br> ' + com[n] + ' in frustum ' + s;
}
}
str += '<br>Total: ' + stats.totalCommands;
}
return str;
}
function boundDepthFrustum(lower, upper, proposed) {
var bounded = Math.min(proposed, upper);
bounded = Math.max(bounded, lower);
return bounded;
}
/**
* The view model for {@link CesiumInspector}.
* @alias CesiumInspectorViewModel
* @constructor
*
* @param {Scene} scene The scene instance to use.
*
* @exception {DeveloperError} scene is required.
*/
function CesiumInspectorViewModel(scene, performanceContainer) {
//>>includeStart('debug', pragmas.debug);
if (!defined(scene)) {
throw new DeveloperError('scene is required');
}
if (!defined(performanceContainer)) {
throw new DeveloperError('performanceContainer is required');
}
//>>includeEnd('debug');
var that = this;
var canvas = scene.canvas;
var eventHandler = new ScreenSpaceEventHandler(canvas);
this._eventHandler = eventHandler;
this._scene = scene;
this._canvas = canvas;
this._primitive = undefined;
this._tile = undefined;
this._modelMatrixPrimitive = undefined;
this._performanceDisplay = undefined;
this._performanceContainer = performanceContainer;
var globe = this._scene.globe;
globe.depthTestAgainstTerrain = true;
/**
* Gets or sets the show frustums state. This property is observable.
* @type {Boolean}
* @default false
*/
this.frustums = false;
/**
* Gets or sets the show performance display state. This property is observable.
* @type {Boolean}
* @default false
*/
this.performance = false;
/**
* Gets or sets the shader cache text. This property is observable.
* @type {String}
* @default ''
*/
this.shaderCacheText = '';
/**
* Gets or sets the show primitive bounding sphere state. This property is observable.
* @type {Boolean}
* @default false
*/
this.primitiveBoundingSphere = false;
/**
* Gets or sets the show primitive reference frame state. This property is observable.
* @type {Boolean}
* @default false
*/
this.primitiveReferenceFrame = false;
/**
* Gets or sets the filter primitive state. This property is observable.
* @type {Boolean}
* @default false
*/
this.filterPrimitive = false;
/**
* Gets or sets the show tile bounding sphere state. This property is observable.
* @type {Boolean}
* @default false
*/
this.tileBoundingSphere = false;
/**
* Gets or sets the filter tile state. This property is observable.
* @type {Boolean}
* @default false
*/
this.filterTile = false;
/**
* Gets or sets the show wireframe state. This property is observable.
* @type {Boolean}
* @default false
*/
this.wireframe = false;
/**
* Gets or sets the show globe depth state. This property is observable.
* @type {Boolean}
* @default false
*/
this.globeDepth = false;
/**
* Gets or sets the show pick depth state. This property is observable.
* @type {Boolean}
* @default false
*/
this.pickDepth = false;
/**
* Gets or sets the index of the depth frustum to display. This property is observable.
* @type {Number}
* @default 1
*/
this.depthFrustum = 1;
this._numberOfFrustums = 1;
/**
* Gets or sets the index of the depth frustum text. This property is observable.
* @type {String}
* @default '1 of 1'
*/
this.depthFrustumText = '1 of 1';
/**
* Gets or sets the suspend updates state. This property is observable.
* @type {Boolean}
* @default false
*/
this.suspendUpdates = false;
/**
* Gets or sets the show tile coordinates state. This property is observable.
* @type {Boolean}
* @default false
*/
this.tileCoordinates = false;
/**
* Gets or sets the frustum statistic text. This property is observable.
* @type {String}
* @default ''
*/
this.frustumStatisticText = '';
/**
* Gets or sets the selected tile information text. This property is observable.
* @type {String}
* @default ''
*/
this.tileText = '';
/**
* Gets if a primitive has been selected. This property is observable.
* @type {Boolean}
* @default false
*/
this.hasPickedPrimitive = false;
/**
* Gets if a tile has been selected. This property is observable
* @type {Boolean}
* @default false
*/
this.hasPickedTile = false;
/**
* Gets if the picking primitive command is active. This property is observable.
* @type {Boolean}
* @default false
*/
this.pickPimitiveActive = false;
/**
* Gets if the picking tile command is active. This property is observable.
* @type {Boolean}
* @default false
*/
this.pickTileActive = false;
/**
* Gets or sets if the cesium inspector drop down is visible. This property is observable.
* @type {Boolean}
* @default true
*/
this.dropDownVisible = true;
/**
* Gets or sets if the general section is visible. This property is observable.
* @type {Boolean}
* @default true
*/
this.generalVisible = true;
/**
* Gets or sets if the primitive section is visible. This property is observable.
* @type {Boolean}
* @default false
*/
this.primitivesVisible = false;
/**
* Gets or sets if the terrain section is visible. This property is observable.
* @type {Boolean}
* @default false
*/
this.terrainVisible = false;
/**
* Gets or sets if the text on the general section expand button. This property is observable.
* @type {String}
* @default '-'
*/
this.generalSwitchText = '-';
/**
* Gets or sets if the text on the primitive section expand button. This property is observable.
* @type {String}
* @default '+'
*/
this.primitivesSwitchText = '+';
/**
* Gets or sets if the text on the terrain section expand button. This property is observable.
* @type {String}
* @default '+'
*/
this.terrainSwitchText = '+';
knockout.track(this, ['filterTile', 'suspendUpdates', 'dropDownVisible', 'shaderCacheText', 'frustums',
'frustumStatisticText', 'pickTileActive', 'pickPrimitiveActive', 'hasPickedPrimitive',
'hasPickedTile', 'tileText', 'generalVisible', 'generalSwitchText',
'primitivesVisible', 'primitivesSwitchText', 'terrainVisible', 'terrainSwitchText', 'depthFrustumText']);
this._toggleDropDown = createCommand(function() {
that.dropDownVisible = !that.dropDownVisible;
});
this._toggleGeneral = createCommand(function() {
that.generalVisible = !that.generalVisible;
that.generalSwitchText = that.generalVisible ? '-' : '+';
});
this._togglePrimitives = createCommand(function() {
that.primitivesVisible = !that.primitivesVisible;
that.primitivesSwitchText = that.primitivesVisible ? '-' : '+';
});
this._toggleTerrain = createCommand(function() {
that.terrainVisible = !that.terrainVisible;
that.terrainSwitchText = that.terrainVisible ? '-' : '+';
});
this._showFrustums = createCommand(function() {
if (that.frustums) {
that._scene.debugShowFrustums = true;
} else {
that._scene.debugShowFrustums = false;
}
return true;
});
this._showPerformance = createCommand(function() {
if (that.performance) {
that._performanceDisplay = new PerformanceDisplay({
container : that._performanceContainer
});
} else {
that._performanceContainer.innerHTML = '';
}
return true;
});
this._showPrimitiveBoundingSphere = createCommand(function() {
that._primitive.debugShowBoundingVolume = that.primitiveBoundingSphere;
return true;
});
this._showPrimitiveReferenceFrame = createCommand(function() {
if (that.primitiveReferenceFrame) {
var modelMatrix = that._primitive.modelMatrix;
that._modelMatrixPrimitive = new DebugModelMatrixPrimitive({
modelMatrix : modelMatrix
});
that._scene.primitives.add(that._modelMatrixPrimitive);
} else if (defined(that._modelMatrixPrimitive)) {
that._scene.primitives.remove(that._modelMatrixPrimitive);
that._modelMatrixPrimitive = undefined;
}
return true;
});
this._doFilterPrimitive = createCommand(function() {
if (that.filterPrimitive) {
that._scene.debugCommandFilter = function(command) {
if (defined(that._modelMatrixPrimitive) && command.owner === that._modelMatrixPrimitive._primitive) {
return true;
} else if (defined(that._primitive)) {
return command.owner === that._primitive || command.owner === that._primitive._billboardCollection || command.owner.primitive === that._primitive;
}
return false;
};
} else {
that._scene.debugCommandFilter = undefined;
}
return true;
});
this._showWireframe = createCommand(function() {
globe._surface.tileProvider._debug.wireframe = that.wireframe;
return true;
});
this._showGlobeDepth = createCommand(function() {
that._scene.debugShowGlobeDepth = that.globeDepth;
return true;
});
this._showPickDepth = createCommand(function() {
that._scene.debugShowPickDepth = that.pickDepth;
return true;
});
this._incrementDepthFrustum = createCommand(function() {
var next = that.depthFrustum + 1;
that.depthFrustum = boundDepthFrustum(1, that._numberOfFrustums, next);
that.scene.debugShowDepthFrustum = that.depthFrustum;
return true;
});
this._decrementDepthFrustum = createCommand(function() {
var next = that.depthFrustum - 1;
that.depthFrustum = boundDepthFrustum(1, that._numberOfFrustums, next);
that.scene.debugShowDepthFrustum = that.depthFrustum;
return true;
});
this._doSuspendUpdates = createCommand(function() {
globe._surface._debug.suspendLodUpdate = that.suspendUpdates;
if (!that.suspendUpdates) {
that.filterTile = false;
}
return true;
});
var tileBoundariesLayer;
this._showTileCoordinates = createCommand(function() {
if (that.tileCoordinates && !defined(tileBoundariesLayer)) {
tileBoundariesLayer = scene.imageryLayers.addImageryProvider(new TileCoordinatesImageryProvider({
tilingScheme : scene.terrainProvider.tilingScheme
}));
} else if (!that.tileCoordinates && defined(tileBoundariesLayer)) {
scene.imageryLayers.remove(tileBoundariesLayer);
tileBoundariesLayer = undefined;
}
return true;
});
this._showTileBoundingSphere = createCommand(function() {
if (that.tileBoundingSphere) {
globe._surface.tileProvider._debug.boundingSphereTile = that._tile;
} else {
globe._surface.tileProvider._debug.boundingSphereTile = undefined;
}
return true;
});
this._doFilterTile = createCommand(function() {
if (!that.filterTile) {
that.suspendUpdates = false;
that.doSuspendUpdates();
} else {
that.suspendUpdates = true;
that.doSuspendUpdates();
globe._surface._tilesToRender = [];
if (defined(that._tile)) {
globe._surface._tilesToRender.push(that._tile);
}
}
return true;
});
function pickPrimitive(e) {
eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
that.pickPrimitiveActive = false;
var newPick = that._scene.pick({
x : e.position.x,
y : e.position.y
});
if (defined(newPick)) {
that.primitive = defined(newPick.collection) ? newPick.collection : newPick.primitive;
}
}
this._pickPrimitive = createCommand(function() {
that.pickPrimitiveActive = !that.pickPrimitiveActive;
if (that.pickPrimitiveActive) {
eventHandler.setInputAction(pickPrimitive, ScreenSpaceEventType.LEFT_CLICK);
} else {
eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
}
});
function selectTile(e) {
var selectedTile;
var ellipsoid = globe.ellipsoid;
var cartesian = that._scene.camera.pickEllipsoid({
x : e.position.x,
y : e.position.y
}, ellipsoid);
if (defined(cartesian)) {
var cartographic = ellipsoid.cartesianToCartographic(cartesian);
var tilesRendered = globe._surface.tileProvider._tilesToRenderByTextureCount;
for (var textureCount = 0; !selectedTile && textureCount < tilesRendered.length; ++textureCount) {
var tilesRenderedByTextureCount = tilesRendered[textureCount];
if (!defined(tilesRenderedByTextureCount)) {
continue;
}
for (var tileIndex = 0; !selectedTile && tileIndex < tilesRenderedByTextureCount.length; ++tileIndex) {
var tile = tilesRenderedByTextureCount[tileIndex];
if (Rectangle.contains(tile.rectangle, cartographic)) {
selectedTile = tile;
}
}
}
}
that.tile = selectedTile;
eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
that.pickTileActive = false;
}
this._pickTile = createCommand(function() {
that.pickTileActive = !that.pickTileActive;
if (that.pickTileActive) {
eventHandler.setInputAction(selectTile, ScreenSpaceEventType.LEFT_CLICK);
} else {
eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
}
});
}
defineProperties(CesiumInspectorViewModel.prototype, {
/**
* Gets the scene to control.
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Scene}
*/
scene : {
get : function() {
return this._scene;
}
},
/**
* Gets the container of the PerformanceDisplay
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Element}
*/
performanceContainer : {
get : function() {
return this._performanceContainer;
}
},
/**
* Gets the command to toggle the visibility of the drop down.
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
toggleDropDown : {
get : function() {
return this._toggleDropDown;
}
},
/**
* Gets the command to toggle {@link Scene.debugShowFrustums}
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showFrustums : {
get : function() {
return this._showFrustums;
}
},
/**
* Gets the command to toggle the visibility of the performance display.
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showPerformance : {
get : function() {
return this._showPerformance;
}
},
/**
* Gets the command to toggle the visibility of a BoundingSphere for a primitive
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showPrimitiveBoundingSphere : {
get : function() {
return this._showPrimitiveBoundingSphere;
}
},
/**
* Gets the command to toggle the visibility of a {@link DebugModelMatrixPrimitive} for the model matrix of a primitive
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showPrimitiveReferenceFrame : {
get : function() {
return this._showPrimitiveReferenceFrame;
}
},
/**
* Gets the command to toggle a filter that renders only a selected primitive
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
doFilterPrimitive : {
get : function() {
return this._doFilterPrimitive;
}
},
/**
* Gets the command to toggle the view of the Globe as a wireframe
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showWireframe : {
get : function() {
return this._showWireframe;
}
},
/**
* Gets the command to toggle the view of the Globe depth buffer
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showGlobeDepth : {
get : function() {
return this._showGlobeDepth;
}
},
/**
* Gets the command to toggle the view of the pick depth buffer
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showPickDepth : {
get : function() {
return this._showPickDepth;
}
},
/**
* Gets the command to increment the depth frustum index to be shown
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
incrementDepthFrustum : {
get : function() {
return this._incrementDepthFrustum;
}
},
/**
* Gets the command to decrement the depth frustum index to be shown
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
decrementDepthFrustum : {
get : function() {
return this._decrementDepthFrustum;
}
},
/**
* Gets the command to toggle whether to suspend tile updates
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
doSuspendUpdates : {
get : function() {
return this._doSuspendUpdates;
}
},
/**
* Gets the command to toggle the visibility of tile coordinates
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showTileCoordinates : {
get : function() {
return this._showTileCoordinates;
}
},
/**
* Gets the command to toggle the visibility of a BoundingSphere for a selected tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
showTileBoundingSphere : {
get : function() {
return this._showTileBoundingSphere;
}
},
/**
* Gets the command to toggle a filter that renders only a selected tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
doFilterTile : {
get : function() {
return this._doFilterTile;
}
},
/**
* Gets the command to expand and collapse the general section
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
toggleGeneral : {
get : function() {
return this._toggleGeneral;
}
},
/**
* Gets the command to expand and collapse the primitives section
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
togglePrimitives : {
get : function() {
return this._togglePrimitives;
}
},
/**
* Gets the command to expand and collapse the terrain section
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
toggleTerrain : {
get : function() {
return this._toggleTerrain;
}
},
/**
* Gets the command to pick a primitive
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
pickPrimitive : {
get : function() {
return this._pickPrimitive;
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
pickTile : {
get : function() {
return this._pickTile;
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
selectParent : {
get : function() {
var that = this;
return createCommand(function() {
that.tile = that.tile.parent;
});
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
selectNW : {
get : function() {
var that = this;
return createCommand(function() {
that.tile = that.tile.children[0];
});
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
selectNE : {
get : function() {
var that = this;
return createCommand(function() {
that.tile = that.tile.children[1];
});
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
selectSW : {
get : function() {
var that = this;
return createCommand(function() {
that.tile = that.tile.children[2];
});
}
},
/**
* Gets the command to pick a tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
selectSE : {
get : function() {
var that = this;
return createCommand(function() {
that.tile = that.tile.children[3];
});
}
},
/**
* Gets or sets the current selected primitive
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
primitive : {
set : function(newPrimitive) {
var oldPrimitive = this._primitive;
if (newPrimitive !== oldPrimitive) {
this.hasPickedPrimitive = true;
if (defined(oldPrimitive)) {
oldPrimitive.debugShowBoundingVolume = false;
}
this._scene.debugCommandFilter = undefined;
if (defined(this._modelMatrixPrimitive)) {
this._scene.primitives.remove(this._modelMatrixPrimitive);
this._modelMatrixPrimitive = undefined;
}
this._primitive = newPrimitive;
newPrimitive.show = false;
setTimeout(function() {
newPrimitive.show = true;
}, 50);
this.showPrimitiveBoundingSphere();
this.showPrimitiveReferenceFrame();
this.doFilterPrimitive();
}
},
get : function() {
return this._primitive;
}
},
/**
* Gets or sets the current selected tile
* @memberof CesiumInspectorViewModel.prototype
*
* @type {Command}
*/
tile : {
set : function(newTile) {
if (defined(newTile)) {
this.hasPickedTile = true;
var oldTile = this._tile;
if (newTile !== oldTile) {
this.tileText = 'L: ' + newTile.level + ' X: ' + newTile.x + ' Y: ' + newTile.y;
this.tileText += '<br>SW corner: ' + newTile.rectangle.west + ', ' + newTile.rectangle.south;
this.tileText += '<br>NE corner: ' + newTile.rectangle.east + ', ' + newTile.rectangle.north;
this.tileText += '<br>Min: ' + newTile.data.minimumHeight + ' Max: ' + newTile.data.maximumHeight;
}
this._tile = newTile;
this.showTileBoundingSphere();
this.doFilterTile();
} else {
this.hasPickedTile = false;
this._tile = undefined;
}
},
get : function() {
return this._tile;
}
},
update : {
get : function() {
var that = this;
return function() {
if (that.frustums) {
that.frustumStatisticText = frustumStatsToString(that._scene.debugFrustumStatistics);
}
// Determine the number of frustums being used.
var numberOfFrustums = that._scene.numberOfFrustums;
that._numberOfFrustums = numberOfFrustums;
// Bound the frustum to be displayed.
var depthFrustum = boundDepthFrustum(1, numberOfFrustums, that.depthFrustum);
that.depthFrustum = depthFrustum;
that.scene.debugShowDepthFrustum = depthFrustum;
// Update the displayed text.
that.depthFrustumText = depthFrustum + ' of ' + numberOfFrustums;
if (that.performance) {
that._performanceDisplay.update();
}
if (that.primitiveReferenceFrame) {
that._modelMatrixPrimitive.modelMatrix = that._primitive.modelMatrix;
}
that.shaderCacheText = 'Cached shaders: ' + that._scene.context.shaderCache.numberOfShaders;
};
}
}
});
/**
* @returns {Boolean} true if the object has been destroyed, false otherwise.
*/
CesiumInspectorViewModel.prototype.isDestroyed = function() {
return false;
};
/**
* Destroys the widget. Should be called if permanently
* removing the widget from layout.
*/
CesiumInspectorViewModel.prototype.destroy = function() {
this._eventHandler.destroy();
return destroyObject(this);
};
return CesiumInspectorViewModel;
});