Add existing to tracked
This commit is contained in:
Generated
Vendored
+310
@@ -0,0 +1,310 @@
|
||||
import Cartesian2 from "../../Core/Cartesian2.js";
|
||||
import Cartesian3 from "../../Core/Cartesian3.js";
|
||||
import defined from "../../Core/defined.js";
|
||||
import Frozen from "../../Core/Frozen.js";
|
||||
import getTimestamp from "../../Core/getTimestamp.js";
|
||||
import CesiumMath from "../../Core/Math.js";
|
||||
import ScreenSpaceEventHandler from "../../Core/ScreenSpaceEventHandler.js";
|
||||
import TimeConstants from "../../Core/TimeConstants.js";
|
||||
import defaultPickWorldPosition from "./defaultPickWorldPosition.js";
|
||||
import ScreenSpaceInputBindings from "./ScreenSpaceInputBindings.js";
|
||||
import MouseButton from "./MouseButton.js";
|
||||
|
||||
/**
|
||||
* @typedef {object} ControllerOptions
|
||||
* @memberof ScreenSpaceMapCameraController
|
||||
* @property {ScreenSpaceInputBindings.InputBinding[]} [dragInputs] The drag input bindings that control panning.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A camera controller that allows panning the camera tangential to the ellipsoid in screen space
|
||||
* by clicking and dragging the mouse.
|
||||
* @class
|
||||
* @implements Controller
|
||||
* @example
|
||||
* viewer.scene.screenSpaceCameraController.enableInputs = false;
|
||||
*
|
||||
* const mapCameraController = new Cesium.ScreenSpaceMapCameraController();
|
||||
* viewer.addController(mapCameraController);
|
||||
*
|
||||
* @example
|
||||
* // Configure the controller to use the right mouse button for panning instead of the default left mouse button.
|
||||
* const mapCameraController = new Cesium.ScreenSpaceMapCameraController({
|
||||
* dragInputs: [{ button: Cesium.MouseButton.RIGHT}]
|
||||
* });
|
||||
* viewer.addController(mapCameraController);
|
||||
*/
|
||||
class ScreenSpaceMapCameraController {
|
||||
/**
|
||||
* @private
|
||||
* @returns {ScreenSpaceInputBindings.InputBinding[]} The default drag input bindings.
|
||||
*/
|
||||
static _getDefaultDragInputs() {
|
||||
return [
|
||||
Object.freeze({
|
||||
button: MouseButton.LEFT,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a ScreenSpaceMapCameraController.
|
||||
* @param {ScreenSpaceMapCameraController.ControllerOptions} [options] The options for configuring the controller.
|
||||
*/
|
||||
constructor(options = Frozen.EMPTY_OBJECT) {
|
||||
this._enabled = true;
|
||||
this._handler = undefined;
|
||||
this._lastUpdateTime = undefined;
|
||||
|
||||
/**
|
||||
* The drag input bindings that map panning. Each binding is a combination of the mouse button
|
||||
* and an optional keyboard modifier.
|
||||
* @type {ScreenSpaceInputBindings.InputBinding[]}
|
||||
* @see ScreenSpaceEventHandler
|
||||
*/
|
||||
this.dragInputs =
|
||||
options.dragInputs ??
|
||||
ScreenSpaceMapCameraController._getDefaultDragInputs();
|
||||
|
||||
this._dragInputState = undefined;
|
||||
this._panDelta = new Cartesian2();
|
||||
this._panPosition = new Cartesian2();
|
||||
|
||||
/**
|
||||
* A callback function used to pick the world position from which to pan. The function is called with {@link Scene}, the {@link Cartesian2} screen space position, and a {@link Cartesian3} instance to store the result. The function should return the {@link Cartesian3} world position from which to pan, or <code>undefined</code> if no position could be picked. If <code>undefined</code> is returned, the camera will pan relative to the ellipsoid surface below the camera.
|
||||
* @type {Function(Scene, Cartesian2, Cartesian3): Cartesian3|undefined}
|
||||
* @default defaultPickWorldPosition
|
||||
* @example
|
||||
* const mapCameraController = new Cesium.ScreenSpaceMapCameraController();
|
||||
* mapCameraController.pickWorldPosition = function (scene, windowPosition, result) {
|
||||
* // Pick the world position from the depth buffer
|
||||
* return scene.pickPosition(windowPosition, result);
|
||||
* };
|
||||
* viewer.addController(mapCameraController);
|
||||
*/
|
||||
this.pickWorldPosition = defaultPickWorldPosition;
|
||||
|
||||
this._ellipsoidNormal = new Cartesian3();
|
||||
this._ellipsoidSurfacePosition = new Cartesian3();
|
||||
this._panDirectionX = new Cartesian3();
|
||||
this._panDirectionY = new Cartesian3();
|
||||
this._pixelSize = new Cartesian2();
|
||||
this._panVelocity = new Cartesian2();
|
||||
|
||||
/**
|
||||
* The speed in meters per pixel at which the camera pans.
|
||||
* @type {number}
|
||||
* @default 1.0
|
||||
*/
|
||||
this.panSpeed = 1.0;
|
||||
|
||||
/**
|
||||
* Enable or disable inertia when panning. When enabled, the camera will continue to move after the user stops dragging, gradually slowing down based on {@link ScreenSpaceMapCameraController#inertialDecay}.
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.inertiaEnabled = true;
|
||||
|
||||
/**
|
||||
* The rate at which the camera's pan velocity decays over time.
|
||||
* @type {number}
|
||||
* @default 6.0
|
||||
*/
|
||||
this.inertialDecay = 6.0;
|
||||
|
||||
/**
|
||||
* A parameter in the range <code>[0, 1)</code> used to limit the range
|
||||
* of inputs to a percentage of the window width/height per animation frame.
|
||||
* This helps keep the camera under control in low-frame-rate situations.
|
||||
* @type {number}
|
||||
* @default 0.1
|
||||
*/
|
||||
this.maximumMovementRatio = 0.1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
set enabled(value) {
|
||||
this._enabled = value;
|
||||
|
||||
if (value) {
|
||||
this._panDelta.x = 0;
|
||||
this._panDelta.y = 0;
|
||||
} else if (defined(this._dragInputState)) {
|
||||
this._dragInputState.isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isDragging() {
|
||||
return defined(this._dragInputState) && this._dragInputState.isDragging;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {HTMLElement} element The DOM element containing the Cesium scene.
|
||||
*/
|
||||
connectedCallback(element) {
|
||||
const handler = new ScreenSpaceEventHandler(element);
|
||||
this._handler = handler;
|
||||
|
||||
this._dragInputState = ScreenSpaceInputBindings.registerDragInputBindings(
|
||||
handler,
|
||||
this.dragInputs,
|
||||
{
|
||||
start: this._handleStartPan.bind(this),
|
||||
change: this._handlePan.bind(this),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {HTMLElement} element The DOM element containing the Cesium scene.
|
||||
*/
|
||||
disconnectedCallback(element) {
|
||||
const handler = this._handler;
|
||||
if (defined(handler) && !handler.isDestroyed()) {
|
||||
handler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
firstUpdate() {
|
||||
this._lastUpdateTime = getTimestamp();
|
||||
this._panDelta.x = 0;
|
||||
this._panDelta.y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {any} scene
|
||||
*/
|
||||
update(scene) {
|
||||
const dt =
|
||||
(getTimestamp() - this._lastUpdateTime) *
|
||||
TimeConstants.SECONDS_PER_MILLISECOND;
|
||||
|
||||
let dx = -this._panDelta.x;
|
||||
let dy = this._panDelta.y;
|
||||
|
||||
if (this.inertiaEnabled && !this.isDragging) {
|
||||
const damping = Math.exp(-this.inertialDecay * dt);
|
||||
this._panVelocity.x *= damping;
|
||||
this._panVelocity.y *= damping;
|
||||
|
||||
dx = this._panVelocity.x * dt;
|
||||
dy = this._panVelocity.y * dt;
|
||||
}
|
||||
|
||||
const { camera, ellipsoid, canvas } = scene;
|
||||
const { clientWidth, clientHeight } = canvas;
|
||||
if (
|
||||
dt === 0 ||
|
||||
clientWidth === 0 ||
|
||||
clientHeight === 0 ||
|
||||
(Math.abs(dx) <= CesiumMath.EPSILON3 &&
|
||||
Math.abs(dy) <= CesiumMath.EPSILON3)
|
||||
) {
|
||||
// Reset for next frame
|
||||
this._lastUpdateTime = getTimestamp();
|
||||
this._panDelta.x = 0;
|
||||
this._panDelta.y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const windowPosition = this._panPosition;
|
||||
let surface = this.pickWorldPosition(
|
||||
scene,
|
||||
windowPosition,
|
||||
this._ellipsoidSurfacePosition,
|
||||
);
|
||||
|
||||
if (!defined(surface)) {
|
||||
surface = ellipsoid.scaleToGeodeticSurface(
|
||||
camera.positionWC,
|
||||
this._ellipsoidSurfacePosition,
|
||||
);
|
||||
}
|
||||
|
||||
const zAxis = ellipsoid.geodeticSurfaceNormal(
|
||||
surface,
|
||||
this._ellipsoidNormal,
|
||||
);
|
||||
|
||||
let xAxis = Cartesian3.clone(camera.rightWC, this._panDirectionX);
|
||||
xAxis = Cartesian3.normalize(xAxis, this._panDirectionX);
|
||||
|
||||
// If z-axis is parallel to camera forward, we use the camera up vector to compute the y-axis. Otherwise, we use the z-axis and x-axis to compute the y-axis.
|
||||
let yAxis = Cartesian3.clone(camera.upWC, this._panDirectionY);
|
||||
const theta = Math.abs(Cartesian3.dot(zAxis, camera.directionWC));
|
||||
if (CesiumMath.lessThan(theta, 1.0, CesiumMath.EPSILON6)) {
|
||||
yAxis = Cartesian3.cross(zAxis, xAxis, this._panDirectionY);
|
||||
}
|
||||
|
||||
yAxis = Cartesian3.normalize(yAxis, this._panDirectionY);
|
||||
|
||||
const distance = Cartesian3.distance(camera.positionWC, surface);
|
||||
const { drawingBufferWidth, drawingBufferHeight, pixelRatio } = scene;
|
||||
const pixelSize = camera.frustum.getPixelDimensions(
|
||||
drawingBufferWidth,
|
||||
drawingBufferHeight,
|
||||
distance,
|
||||
pixelRatio,
|
||||
this._pixelSize,
|
||||
);
|
||||
|
||||
const maxPixels =
|
||||
this.maximumMovementRatio * Math.max(clientWidth, clientHeight);
|
||||
|
||||
dx = CesiumMath.clamp(dx, -maxPixels, maxPixels);
|
||||
this._panVelocity.x = dx / dt;
|
||||
dx *= this.panSpeed * pixelSize.x;
|
||||
|
||||
dy = CesiumMath.clamp(dy, -maxPixels, maxPixels);
|
||||
this._panVelocity.y = dy / dt;
|
||||
dy *= this.panSpeed * pixelSize.y;
|
||||
|
||||
camera.move(xAxis, dx);
|
||||
camera.move(yAxis, dy);
|
||||
|
||||
// Reset for next frame
|
||||
this._lastUpdateTime = getTimestamp();
|
||||
this._panDelta.x = 0;
|
||||
this._panDelta.y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} event
|
||||
*/
|
||||
_handleStartPan(event) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._panDelta.x = 0;
|
||||
this._panDelta.y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_handlePan(event) {
|
||||
this._panDelta.x += event.endPosition.x - event.startPosition.x;
|
||||
this._panDelta.y += event.endPosition.y - event.startPosition.y;
|
||||
this._panPosition.x = event.endPosition.x;
|
||||
this._panPosition.y = event.endPosition.y;
|
||||
}
|
||||
}
|
||||
|
||||
export default ScreenSpaceMapCameraController;
|
||||
Reference in New Issue
Block a user