/*global define*/
define([
'../Core/BoundingRectangle',
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
'../Core/Cartographic',
'../Core/defined',
'../Core/DeveloperError',
'../Core/Math',
'../Core/Matrix4',
'./SceneMode'
], function(
BoundingRectangle,
Cartesian2,
Cartesian3,
Cartesian4,
Cartographic,
defined,
DeveloperError,
CesiumMath,
Matrix4,
SceneMode) {
"use strict";
/**
* Functions that do scene-dependent transforms between rendering-related coordinate systems.
*
* @namespace
* @alias SceneTransforms
*/
var SceneTransforms = {};
var actualPositionScratch = new Cartesian4(0, 0, 0, 1);
var positionCC = new Cartesian4();
var viewProjectionScratch = new Matrix4();
/**
* Transforms a position in WGS84 coordinates to window coordinates. This is commonly used to place an
* HTML element at the same screen position as an object in the scene.
*
* @param {Scene} scene The scene.
* @param {Cartesian3} position The position in WGS84 (world) coordinates.
* @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates.
* @returns {Cartesian2} The modified result parameter or a new Cartesian3 instance if one was not provided. This may be undefined
if the input position is near the center of the ellipsoid.
*
* @example
* // Output the window position of longitude/latitude (0, 0) every time the mouse moves.
* var scene = widget.scene;
* var ellipsoid = scene.globe.ellipsoid;
* var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0));
* var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
* handler.setInputAction(function(movement) {
* console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position));
* }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
*/
SceneTransforms.wgs84ToWindowCoordinates = function(scene, position, result) {
//>>includeStart('debug', pragmas.debug);
if (!defined(scene)) {
throw new DeveloperError('scene is required.');
}
if (!defined(position)) {
throw new DeveloperError('position is required.');
}
//>>includeEnd('debug');
// Transform for 3D, 2D, or Columbus view
var actualPosition = SceneTransforms.computeActualWgs84Position(scene.frameState, position, actualPositionScratch);
if (!defined(actualPosition)) {
return undefined;
}
// View-projection matrix to transform from world coordinates to clip coordinates
var camera = scene.camera;
var viewProjection = Matrix4.multiply(camera.frustum.projectionMatrix, camera.viewMatrix, viewProjectionScratch);
Matrix4.multiplyByVector(viewProjection, Cartesian4.fromElements(actualPosition.x, actualPosition.y, actualPosition.z, 1, positionCC), positionCC);
if ((positionCC.z < 0) && (scene.mode !== SceneMode.SCENE2D)) {
return undefined;
}
result = SceneTransforms.clipToGLWindowCoordinates(scene, positionCC, result);
result.y = scene.canvas.clientHeight - result.y;
return result;
};
/**
* Transforms a position in WGS84 coordinates to drawing buffer coordinates. This may produce different
* results from SceneTransforms.wgs84ToWindowCoordinates when the browser zoom is not 100%, or on high-DPI displays.
*
* @param {Scene} scene The scene.
* @param {Cartesian3} position The position in WGS84 (world) coordinates.
* @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates.
* @returns {Cartesian2} The modified result parameter or a new Cartesian3 instance if one was not provided. This may be undefined
if the input position is near the center of the ellipsoid.
*
* @example
* // Output the window position of longitude/latitude (0, 0) every time the mouse moves.
* var scene = widget.scene;
* var ellipsoid = scene.globe.ellipsoid;
* var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0));
* var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
* handler.setInputAction(function(movement) {
* console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position));
* }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
*/
SceneTransforms.wgs84ToDrawingBufferCoordinates = function(scene, position, result) {
//>>includeStart('debug', pragmas.debug);
if (!defined(scene)) {
throw new DeveloperError('scene is required.');
}
if (!defined(position)) {
throw new DeveloperError('position is required.');
}
//>>includeEnd('debug');
// Transform for 3D, 2D, or Columbus view
var actualPosition = SceneTransforms.computeActualWgs84Position(scene.frameState, position, actualPositionScratch);
if (!defined(actualPosition)) {
return undefined;
}
// View-projection matrix to transform from world coordinates to clip coordinates
var camera = scene.camera;
var viewProjection = Matrix4.multiply(camera.frustum.projectionMatrix, camera.viewMatrix, viewProjectionScratch);
Matrix4.multiplyByVector(viewProjection, Cartesian4.fromElements(actualPosition.x, actualPosition.y, actualPosition.z, 1, positionCC), positionCC);
if ((positionCC.z < 0) && (scene.mode !== SceneMode.SCENE2D)) {
return undefined;
}
return SceneTransforms.clipToDrawingBufferCoordinates(scene, positionCC, result);
};
var projectedPosition = new Cartesian3();
var positionInCartographic = new Cartographic();
/**
* @private
*/
SceneTransforms.computeActualWgs84Position = function(frameState, position, result) {
var mode = frameState.mode;
if (mode === SceneMode.SCENE3D) {
return Cartesian3.clone(position, result);
}
var projection = frameState.mapProjection;
var cartographic = projection.ellipsoid.cartesianToCartographic(position, positionInCartographic);
if (!defined(cartographic)) {
return undefined;
}
projection.project(cartographic, projectedPosition);
if (mode === SceneMode.COLUMBUS_VIEW) {
return Cartesian3.fromElements(projectedPosition.z, projectedPosition.x, projectedPosition.y, result);
}
if (mode === SceneMode.SCENE2D) {
return Cartesian3.fromElements(0.0, projectedPosition.x, projectedPosition.y, result);
}
// mode === SceneMode.MORPHING
var morphTime = frameState.morphTime;
return Cartesian3.fromElements(
CesiumMath.lerp(projectedPosition.z, position.x, morphTime),
CesiumMath.lerp(projectedPosition.x, position.y, morphTime),
CesiumMath.lerp(projectedPosition.y, position.z, morphTime),
result);
};
var positionNDC = new Cartesian3();
var positionWC = new Cartesian3();
var viewport = new BoundingRectangle();
var viewportTransform = new Matrix4();
/**
* @private
*/
SceneTransforms.clipToGLWindowCoordinates = function(scene, position, result) {
var canvas = scene.canvas;
// Perspective divide to transform from clip coordinates to normalized device coordinates
Cartesian3.divideByScalar(position, position.w, positionNDC);
// Assuming viewport takes up the entire canvas...
viewport.width = canvas.clientWidth;
viewport.height = canvas.clientHeight;
Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform);
// Viewport transform to transform from clip coordinates to window coordinates
Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC);
return Cartesian2.fromCartesian3(positionWC, result);
};
/**
* @private
*/
SceneTransforms.clipToDrawingBufferCoordinates = function(scene, position, result) {
// Perspective divide to transform from clip coordinates to normalized device coordinates
Cartesian3.divideByScalar(position, position.w, positionNDC);
// Assuming viewport takes up the entire canvas...
viewport.width = scene.drawingBufferWidth;
viewport.height = scene.drawingBufferHeight;
Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform);
// Viewport transform to transform from clip coordinates to drawing buffer coordinates
Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC);
return Cartesian2.fromCartesian3(positionWC, result);
};
/**
* @private
*/
SceneTransforms.transformWindowToDrawingBuffer = function(scene, windowPosition, result) {
var canvas = scene.canvas;
var xScale = scene.drawingBufferWidth / canvas.clientWidth;
var yScale = scene.drawingBufferHeight / canvas.clientHeight;
return Cartesian2.fromElements(windowPosition.x * xScale, windowPosition.y * yScale, result);
};
return SceneTransforms;
});