@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
986 lines (985 loc) • 94.4 kB
JavaScript
/** This file must only contain pure code and pure imports */
import { __esDecorate, __runInitializers } from "../tslib.es6.js";
import { serialize, serializeAsVector3, serializeAsMeshReference, serializeAsVector2 } from "../Misc/decorators.js";
import { Observable } from "../Misc/observable.pure.js";
import { Matrix, Vector3, Vector2, TmpVectors, Quaternion } from "../Maths/math.vector.pure.js";
import { Clamp } from "../Maths/math.scalar.functions.js";
import { Node } from "../node.js";
import { Mesh } from "../Meshes/mesh.pure.js";
import { AutoRotationBehavior } from "../Behaviors/Cameras/autoRotationBehavior.js";
import { BouncingBehavior } from "../Behaviors/Cameras/bouncingBehavior.js";
import { FramingBehavior } from "../Behaviors/Cameras/framingBehavior.js";
import { Camera } from "./camera.pure.js";
import { TargetCamera } from "./targetCamera.pure.js";
import { ArcRotateCameraInputsManager } from "../Cameras/arcRotateCameraInputsManager.pure.js";
import { Epsilon } from "../Maths/math.constants.js";
import { Tools } from "../Misc/tools.pure.js";
import { RegisterClass } from "../Misc/typeStore.js";
import { ArcRotateCameraMovement } from "./arcRotateCameraMovement.js";
/**
* Computes the alpha angle based on the source position and the target position.
* @param offset The directional offset between the source position and the target position
* @returns The alpha angle in radians
*/
export function ComputeAlpha(offset) {
// Default alpha to π/2 to handle the edge case where x and z are both zero (when looking along up axis)
let alpha = Math.PI / 2;
if (!(offset.x === 0 && offset.z === 0)) {
alpha = Math.acos(offset.x / Math.sqrt(Math.pow(offset.x, 2) + Math.pow(offset.z, 2)));
}
if (offset.z < 0) {
alpha = 2 * Math.PI - alpha;
}
return alpha;
}
/**
* Computes the beta angle based on the source position and the target position.
* @param verticalOffset The y value of the directional offset between the source position and the target position
* @param radius The distance between the source position and the target position
* @returns The beta angle in radians
*/
export function ComputeBeta(verticalOffset, radius) {
return Math.acos(verticalOffset / radius);
}
// Returns the value if not NaN, otherwise returns the fallback value.
function CheckNaN(value, fallback) {
return isNaN(value) ? fallback : value;
}
/**
* This represents an orbital type of camera.
*
* This camera always points towards a given target position and can be rotated around that target with the target as the centre of rotation. It can be controlled with cursors and mouse, or with touch events.
* Think of this camera as one orbiting its target position, or more imaginatively as a spy satellite orbiting the earth. Its position relative to the target (earth) can be set by three parameters, alpha (radians) the longitudinal rotation, beta (radians) the latitudinal rotation and radius the distance from the target position.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction#arc-rotate-camera
*/
let ArcRotateCamera = (() => {
var _a;
let _classSuper = TargetCamera;
let _instanceExtraInitializers = [];
let _alpha_decorators;
let _alpha_initializers = [];
let _alpha_extraInitializers = [];
let _beta_decorators;
let _beta_initializers = [];
let _beta_extraInitializers = [];
let _radius_decorators;
let _radius_initializers = [];
let _radius_extraInitializers = [];
let _overrideCloneAlphaBetaRadius_decorators;
let _overrideCloneAlphaBetaRadius_initializers = [];
let _overrideCloneAlphaBetaRadius_extraInitializers = [];
let __target_decorators;
let __target_initializers = [];
let __target_extraInitializers = [];
let __targetHost_decorators;
let __targetHost_initializers = [];
let __targetHost_extraInitializers = [];
let _get_inertialAlphaOffset_decorators;
let _get_inertialBetaOffset_decorators;
let _get_inertialRadiusOffset_decorators;
let _lowerAlphaLimit_decorators;
let _lowerAlphaLimit_initializers = [];
let _lowerAlphaLimit_extraInitializers = [];
let _upperAlphaLimit_decorators;
let _upperAlphaLimit_initializers = [];
let _upperAlphaLimit_extraInitializers = [];
let _lowerBetaLimit_decorators;
let _lowerBetaLimit_initializers = [];
let _lowerBetaLimit_extraInitializers = [];
let _upperBetaLimit_decorators;
let _upperBetaLimit_initializers = [];
let _upperBetaLimit_extraInitializers = [];
let _lowerRadiusLimit_decorators;
let _lowerRadiusLimit_initializers = [];
let _lowerRadiusLimit_extraInitializers = [];
let _upperRadiusLimit_decorators;
let _upperRadiusLimit_initializers = [];
let _upperRadiusLimit_extraInitializers = [];
let _lowerTargetYLimit_decorators;
let _lowerTargetYLimit_initializers = [];
let _lowerTargetYLimit_extraInitializers = [];
let _inertialPanningX_decorators;
let _inertialPanningX_initializers = [];
let _inertialPanningX_extraInitializers = [];
let _inertialPanningY_decorators;
let _inertialPanningY_initializers = [];
let _inertialPanningY_extraInitializers = [];
let _pinchToPanMaxDistance_decorators;
let _pinchToPanMaxDistance_initializers = [];
let _pinchToPanMaxDistance_extraInitializers = [];
let _panningDistanceLimit_decorators;
let _panningDistanceLimit_initializers = [];
let _panningDistanceLimit_extraInitializers = [];
let _panningOriginTarget_decorators;
let _panningOriginTarget_initializers = [];
let _panningOriginTarget_extraInitializers = [];
let _get_panningInertia_decorators;
let _get_zoomToMouseLocation_decorators;
let _zoomOnFactor_decorators;
let _zoomOnFactor_initializers = [];
let _zoomOnFactor_extraInitializers = [];
let _targetScreenOffset_decorators;
let _targetScreenOffset_initializers = [];
let _targetScreenOffset_extraInitializers = [];
let _allowUpsideDown_decorators;
let _allowUpsideDown_initializers = [];
let _allowUpsideDown_extraInitializers = [];
let _useInputToRestoreState_decorators;
let _useInputToRestoreState_initializers = [];
let _useInputToRestoreState_extraInitializers = [];
let _restoreStateInterpolationFactor_decorators;
let _restoreStateInterpolationFactor_initializers = [];
let _restoreStateInterpolationFactor_extraInitializers = [];
return _a = class ArcRotateCamera extends _classSuper {
/**
* Defines the target point of the camera.
* The camera looks towards it from the radius distance.
*/
get target() {
return this._target;
}
set target(value) {
this.setTarget(value);
}
/**
* Defines the target transform node of the camera.
* The camera looks towards it from the radius distance.
* Please note that setting a target host will disable panning.
*/
get targetHost() {
return this._targetHost;
}
set targetHost(value) {
if (value) {
this.setTarget(value);
}
}
/**
* Return the current target position of the camera. This value is expressed in local space.
* @returns the target position
*/
getTarget() {
return this.target;
}
/**
* Define the current local position of the camera in the scene
*/
get position() {
return this._position;
}
set position(newPosition) {
this.setPosition(newPosition);
}
/**
* The vector the camera should consider as up. (default is Vector3(0, 1, 0) as returned by Vector3.Up())
* Setting this will copy the given vector to the camera's upVector, and set rotation matrices to and from Y up.
* DO NOT set the up vector using copyFrom or copyFromFloats, as this bypasses setting the above matrices.
*/
set upVector(vec) {
if (!this._upToYMatrix) {
this._yToUpMatrix = new Matrix();
this._upToYMatrix = new Matrix();
this._upVector = Vector3.Zero();
}
vec.normalize();
this._upVector.copyFrom(vec);
this.setMatUp();
}
get upVector() {
return this._upVector;
}
/**
* Sets the Y-up to camera up-vector rotation matrix, and the up-vector to Y-up rotation matrix.
*/
setMatUp() {
// from y-up to custom-up (used in _getViewMatrix)
Matrix.RotationAlignToRef(Vector3.UpReadOnly, this._upVector, this._yToUpMatrix);
// from custom-up to y-up (used in rebuildAnglesAndRadius)
Matrix.RotationAlignToRef(this._upVector, Vector3.UpReadOnly, this._upToYMatrix);
}
/**
* Current inertia value on the longitudinal axis.
* When nonzero, represents the per-frame angular offset (in radians) applied to `alpha`.
* Each frame, this value is multiplied by {@link inertia} (a decay coefficient where
* 0 = instant stop, 0.9 = smooth glide, 1 = never stops).
* Reading this value also reflects the rotation delta the movement system will apply this frame
* (decays toward 0 over the inertia tail), preserving legacy semantics for "is the camera still animating?" checks.
* Setting this to 0 also stops the movement system's rotation velocity for backward compatibility.
*/
get inertialAlphaOffset() {
if (this._inertialAlphaOffset !== 0) {
return this._inertialAlphaOffset;
}
if (this.movement.rotationAccumulatedPixels.x !== 0) {
return this.movement.rotationAccumulatedPixels.x;
}
const delta = this.movement.rotationDeltaCurrentFrame.x;
return Math.abs(delta) < this._rotationEpsilon ? 0 : delta;
}
set inertialAlphaOffset(value) {
this._inertialAlphaOffset = value;
if (value === 0) {
this.movement.resetRotationVelocity();
}
}
/**
* Current inertia value on the latitudinal axis.
* When nonzero, represents the per-frame angular offset (in radians) applied to `beta`.
* Each frame, this value is multiplied by {@link inertia} (a decay coefficient where
* 0 = instant stop, 0.9 = smooth glide, 1 = never stops).
* Reading this value also reflects the rotation delta the movement system will apply this frame
* (decays toward 0 over the inertia tail), preserving legacy semantics for "is the camera still animating?" checks.
* Setting this to 0 also stops the movement system's rotation velocity for backward compatibility.
*/
get inertialBetaOffset() {
if (this._inertialBetaOffset !== 0) {
return this._inertialBetaOffset;
}
if (this.movement.rotationAccumulatedPixels.y !== 0) {
return this.movement.rotationAccumulatedPixels.y;
}
const delta = this.movement.rotationDeltaCurrentFrame.y;
return Math.abs(delta) < this._rotationEpsilon ? 0 : delta;
}
set inertialBetaOffset(value) {
this._inertialBetaOffset = value;
if (value === 0) {
this.movement.resetRotationVelocity();
}
}
/**
* Current inertia value on the radius axis.
* When nonzero, represents the per-frame offset (in scene units) applied to `radius`.
* Each frame, this value is multiplied by {@link inertia} (a decay coefficient where
* 0 = instant stop, 0.9 = smooth glide, 1 = never stops).
* Reading this value also reflects the zoom delta the movement system will apply this frame
* (decays toward 0 over the inertia tail), preserving legacy semantics for "is the camera still animating?" checks.
* Setting this to 0 also stops the movement system's zoom velocity for backward compatibility.
*/
get inertialRadiusOffset() {
if (this._inertialRadiusOffset !== 0) {
return this._inertialRadiusOffset;
}
if (this.movement.zoomAccumulatedPixels !== 0) {
return this.movement.zoomAccumulatedPixels;
}
const delta = this.movement.zoomDeltaCurrentFrame;
return Math.abs(delta) < this._rotationEpsilon ? 0 : delta;
}
set inertialRadiusOffset(value) {
this._inertialRadiusOffset = value;
if (value === 0) {
this.movement.resetZoomVelocity();
}
}
/**
* Defines the value of the inertia used during panning.
* A decay coefficient applied per reference frame at 60fps:
* 0 means stop instantly, 0.9 means smooth glide, 1 means never stop.
* Setting this also updates the movement system's pan inertia.
*/
get panningInertia() {
return this._panningInertia;
}
set panningInertia(value) {
this._panningInertia = value;
if (this.movement) {
this.movement.panInertia = value;
}
}
/**
* Defines the rotation/zoom inertia (decay coefficient applied per reference frame at 60fps).
* Override of {@link Camera.inertia} that automatically syncs to the movement system
* (rotation and zoom). Panning inertia is controlled separately via {@link panningInertia}.
*/
get inertia() {
return this._inertia;
}
set inertia(value) {
this._inertia = value;
if (this.movement) {
this.movement.rotationInertia = value;
this.movement.zoomInertia = value;
}
}
//-- begin properties for backward compatibility for inputs
/**
* Gets or Set the pointer angular sensibility along the X axis or how fast is the camera rotating.
*/
get angularSensibilityX() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.angularSensibilityX;
}
return 0;
}
set angularSensibilityX(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.angularSensibilityX = value;
}
}
/**
* Gets or Set the pointer angular sensibility along the Y axis or how fast is the camera rotating.
*/
get angularSensibilityY() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.angularSensibilityY;
}
return 0;
}
set angularSensibilityY(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.angularSensibilityY = value;
}
}
/**
* Gets or Set the pointer pinch precision or how fast is the camera zooming.
*/
get pinchPrecision() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.pinchPrecision;
}
return 0;
}
set pinchPrecision(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.pinchPrecision = value;
}
}
/**
* Gets or Set the pointer pinch delta percentage or how fast is the camera zooming.
* It will be used instead of pinchPrecision if different from 0.
* It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
*/
get pinchDeltaPercentage() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.pinchDeltaPercentage;
}
return 0;
}
set pinchDeltaPercentage(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.pinchDeltaPercentage = value;
}
}
/**
* Gets or Set the pointer use natural pinch zoom to override the pinch precision
* and pinch delta percentage.
* When useNaturalPinchZoom is true, multi touch zoom will zoom in such
* that any object in the plane at the camera's target point will scale
* perfectly with finger motion.
*/
get useNaturalPinchZoom() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.useNaturalPinchZoom;
}
return false;
}
set useNaturalPinchZoom(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.useNaturalPinchZoom = value;
}
}
/**
* Gets or Set the pointer panning sensibility or how fast is the camera moving.
*/
get panningSensibility() {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
return pointers.panningSensibility;
}
return 0;
}
set panningSensibility(value) {
const pointers = this.inputs.attached["pointers"];
if (pointers) {
pointers.panningSensibility = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control beta angle in a positive direction.
*/
get keysUp() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysUp;
}
return [];
}
set keysUp(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysUp = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control beta angle in a negative direction.
*/
get keysDown() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysDown;
}
return [];
}
set keysDown(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysDown = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control alpha angle in a negative direction.
*/
get keysLeft() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysLeft;
}
return [];
}
set keysLeft(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysLeft = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control alpha angle in a positive direction.
*/
get keysRight() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRight;
}
return [];
}
set keysRight(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRight = value;
}
}
/**
* Gets or Set the mouse wheel precision or how fast is the camera zooming.
*/
get wheelPrecision() {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
return mousewheel.wheelPrecision;
}
return 0;
}
set wheelPrecision(value) {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
mousewheel.wheelPrecision = value;
}
}
/**
* Gets or Set the boolean value that controls whether or not the mouse wheel
* zooms to the location of the mouse pointer or not. The default is false.
*/
get zoomToMouseLocation() {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
return mousewheel.zoomToMouseLocation;
}
return false;
}
set zoomToMouseLocation(value) {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
mousewheel.zoomToMouseLocation = value;
}
}
/**
* Gets or Set the mouse wheel delta percentage or how fast is the camera zooming.
* It will be used instead of wheelPrecision if different from 0.
* It defines the percentage of current camera.radius to use as delta when wheel zoom is used.
*/
get wheelDeltaPercentage() {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
return mousewheel.wheelDeltaPercentage;
}
return 0;
}
set wheelDeltaPercentage(value) {
const mousewheel = this.inputs.attached["mousewheel"];
if (mousewheel) {
mousewheel.wheelDeltaPercentage = value;
}
}
/**
* Gets or sets whether ctrl+keyboard triggers panning.
* Setting this updates the keyboard→pan inputMap entry.
* @internal kept for backward compatibility
*/
get _useCtrlForPanning() {
return this._useCtrlForPanningInternal;
}
set _useCtrlForPanning(value) {
this._useCtrlForPanningInternal = value;
const input = this.movement.input;
// Manage keyboard ctrl → pan entry
const keyboardEntry = input.getEntry("keyboard", "pan", { modifiers: { ctrl: true } });
if (!value && keyboardEntry) {
input.inputMap.splice(input.inputMap.indexOf(keyboardEntry), 1);
}
else if (value && !keyboardEntry) {
input.addEntry({ source: "keyboard", modifiers: { ctrl: true }, interaction: "pan" });
}
// Manage pointer ctrl+left-drag → pan entry (matches legacy ArcRotateCameraPointersInput behavior)
const pointerEntry = input.getEntry("pointer", "pan", { modifiers: { ctrl: true } });
if (!value && pointerEntry) {
input.inputMap.splice(input.inputMap.indexOf(pointerEntry), 1);
}
else if (value && !pointerEntry) {
input.addEntry({ source: "pointer", button: 0, modifiers: { ctrl: true }, interaction: "pan" });
}
}
/**
* Gets or sets which mouse button triggers panning (0=left, 1=middle, 2=right).
* Setting this updates the pointer→pan inputMap entry.
* @internal kept for backward compatibility with attachControl signature
*/
get _panningMouseButton() {
return this._panningMouseButtonInternal;
}
set _panningMouseButton(value) {
this._panningMouseButtonInternal = value;
const entry = this.movement.input.getEntry("pointer", "pan", { modifiers: {} });
if (entry) {
entry.button = value;
}
}
/**
* If true, indicates the camera is currently interpolating to a new pose.
*/
get isInterpolating() {
return this._isInterpolating;
}
/**
* Gets the bouncing behavior of the camera if it has been enabled.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#bouncing-behavior
*/
get bouncingBehavior() {
return this._bouncingBehavior;
}
/**
* Defines if the bouncing behavior of the camera is enabled on the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#bouncing-behavior
*/
get useBouncingBehavior() {
return this._bouncingBehavior != null;
}
set useBouncingBehavior(value) {
if (value === this.useBouncingBehavior) {
return;
}
if (value) {
this._bouncingBehavior = new BouncingBehavior();
this.addBehavior(this._bouncingBehavior);
}
else if (this._bouncingBehavior) {
this.removeBehavior(this._bouncingBehavior);
this._bouncingBehavior = null;
}
}
/**
* Gets the framing behavior of the camera if it has been enabled.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#framing-behavior
*/
get framingBehavior() {
return this._framingBehavior;
}
/**
* Defines if the framing behavior of the camera is enabled on the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#framing-behavior
*/
get useFramingBehavior() {
return this._framingBehavior != null;
}
set useFramingBehavior(value) {
if (value === this.useFramingBehavior) {
return;
}
if (value) {
this._framingBehavior = new FramingBehavior();
this.addBehavior(this._framingBehavior);
}
else if (this._framingBehavior) {
this.removeBehavior(this._framingBehavior);
this._framingBehavior = null;
}
}
/**
* Gets the auto rotation behavior of the camera if it has been enabled.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#autorotation-behavior
*/
get autoRotationBehavior() {
return this._autoRotationBehavior;
}
/**
* Defines if the auto rotation behavior of the camera is enabled on the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors#autorotation-behavior
*/
get useAutoRotationBehavior() {
return this._autoRotationBehavior != null;
}
set useAutoRotationBehavior(value) {
if (value === this.useAutoRotationBehavior) {
return;
}
if (value) {
this._autoRotationBehavior = new AutoRotationBehavior();
this.addBehavior(this._autoRotationBehavior);
}
else if (this._autoRotationBehavior) {
this.removeBehavior(this._autoRotationBehavior);
this._autoRotationBehavior = null;
}
}
/**
* Instantiates a new ArcRotateCamera in a given scene
* @param name Defines the name of the camera
* @param alpha Defines the camera rotation along the longitudinal axis
* @param beta Defines the camera rotation along the latitudinal axis
* @param radius Defines the camera distance from its target
* @param target Defines the camera target
* @param scene Defines the scene the camera belongs to
* @param setActiveOnSceneIfNoneActive Defines whether the camera should be marked as active if not other active cameras have been defined
*/
constructor(name, alpha, beta, radius, target, scene, setActiveOnSceneIfNoneActive = true) {
super(name, Vector3.Zero(), scene, setActiveOnSceneIfNoneActive);
/**
* Defines the rotation angle of the camera along the longitudinal axis.
*/
this.alpha = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _alpha_initializers, void 0));
/**
* Defines the rotation angle of the camera along the latitudinal axis.
*/
this.beta = (__runInitializers(this, _alpha_extraInitializers), __runInitializers(this, _beta_initializers, void 0));
/**
* Defines the radius of the camera from its target point.
*/
this.radius = (__runInitializers(this, _beta_extraInitializers), __runInitializers(this, _radius_initializers, void 0));
/**
* Defines an override value to use as the parameter to setTarget.
* This allows the parameter to be specified when animating the target (e.g. using FramingBehavior).
*/
this.overrideCloneAlphaBetaRadius = (__runInitializers(this, _radius_extraInitializers), __runInitializers(this, _overrideCloneAlphaBetaRadius_initializers, void 0));
this._target = (__runInitializers(this, _overrideCloneAlphaBetaRadius_extraInitializers), __runInitializers(this, __target_initializers, void 0));
this._targetHost = (__runInitializers(this, __target_extraInitializers), __runInitializers(this, __targetHost_initializers, void 0));
// eslint-disable-next-line @typescript-eslint/naming-convention
this._upToYMatrix = __runInitializers(this, __targetHost_extraInitializers);
this._inertialAlphaOffset = 0;
this._inertialBetaOffset = 0;
this._inertialRadiusOffset = 0;
/**
* Minimum allowed angle on the longitudinal axis.
* This can help limiting how the Camera is able to move in the scene.
*/
this.lowerAlphaLimit = __runInitializers(this, _lowerAlphaLimit_initializers, null);
/**
* Maximum allowed angle on the longitudinal axis.
* This can help limiting how the Camera is able to move in the scene.
*/
this.upperAlphaLimit = (__runInitializers(this, _lowerAlphaLimit_extraInitializers), __runInitializers(this, _upperAlphaLimit_initializers, null));
/**
* Minimum allowed angle on the latitudinal axis.
* This can help limiting how the Camera is able to move in the scene.
*/
this.lowerBetaLimit = (__runInitializers(this, _upperAlphaLimit_extraInitializers), __runInitializers(this, _lowerBetaLimit_initializers, 0.01));
/**
* Maximum allowed angle on the latitudinal axis.
* This can help limiting how the Camera is able to move in the scene.
*/
this.upperBetaLimit = (__runInitializers(this, _lowerBetaLimit_extraInitializers), __runInitializers(this, _upperBetaLimit_initializers, Math.PI - 0.01));
/**
* Minimum allowed distance of the camera to the target (The camera can not get closer).
* This can help limiting how the Camera is able to move in the scene.
*/
this.lowerRadiusLimit = (__runInitializers(this, _upperBetaLimit_extraInitializers), __runInitializers(this, _lowerRadiusLimit_initializers, null));
/**
* Maximum allowed distance of the camera to the target (The camera can not get further).
* This can help limiting how the Camera is able to move in the scene.
*/
this.upperRadiusLimit = (__runInitializers(this, _lowerRadiusLimit_extraInitializers), __runInitializers(this, _upperRadiusLimit_initializers, null));
/**
* Minimum allowed vertical target position of the camera.
* Use this setting in combination with `upperRadiusLimit` to set a global limit for the Cameras vertical position.
*/
this.lowerTargetYLimit = (__runInitializers(this, _upperRadiusLimit_extraInitializers), __runInitializers(this, _lowerTargetYLimit_initializers, -Infinity));
/**
* Defines the current inertia value used during panning of the camera along the X axis.
*/
this.inertialPanningX = (__runInitializers(this, _lowerTargetYLimit_extraInitializers), __runInitializers(this, _inertialPanningX_initializers, 0));
/**
* Defines the current inertia value used during panning of the camera along the Y axis.
*/
this.inertialPanningY = (__runInitializers(this, _inertialPanningX_extraInitializers), __runInitializers(this, _inertialPanningY_initializers, 0));
/**
* Defines the distance used to consider the camera in pan mode vs pinch/zoom.
* Basically if your fingers moves away from more than this distance you will be considered
* in pinch mode.
*/
this.pinchToPanMaxDistance = (__runInitializers(this, _inertialPanningY_extraInitializers), __runInitializers(this, _pinchToPanMaxDistance_initializers, 20));
/**
* Defines the maximum distance the camera can pan.
* This could help keeping the camera always in your scene.
*/
this.panningDistanceLimit = (__runInitializers(this, _pinchToPanMaxDistance_extraInitializers), __runInitializers(this, _panningDistanceLimit_initializers, null));
/**
* Defines the target of the camera before panning.
*/
this.panningOriginTarget = (__runInitializers(this, _panningDistanceLimit_extraInitializers), __runInitializers(this, _panningOriginTarget_initializers, Vector3.Zero()));
this._panningInertia = (__runInitializers(this, _panningOriginTarget_extraInitializers), 0.9);
this._inertia = 0.9;
//-- end properties for backward compatibility for inputs
/**
* Defines how much the radius should be scaled while zooming on a particular mesh (through the zoomOn function)
*/
this.zoomOnFactor = __runInitializers(this, _zoomOnFactor_initializers, 1);
/**
* Defines a screen offset for the camera position.
*/
this.targetScreenOffset = (__runInitializers(this, _zoomOnFactor_extraInitializers), __runInitializers(this, _targetScreenOffset_initializers, Vector2.Zero()));
/**
* Allows the camera to be completely reversed.
* If false the camera can not arrive upside down.
*/
this.allowUpsideDown = (__runInitializers(this, _targetScreenOffset_extraInitializers), __runInitializers(this, _allowUpsideDown_initializers, true));
/**
* Define if double tap/click is used to restore the previously saved state of the camera.
*/
this.useInputToRestoreState = (__runInitializers(this, _allowUpsideDown_extraInitializers), __runInitializers(this, _useInputToRestoreState_initializers, true));
/**
* Factor for restoring information interpolation. default is 0 = off. Any value \< 0 or \> 1 will disable interpolation.
*/
this.restoreStateInterpolationFactor = (__runInitializers(this, _useInputToRestoreState_extraInitializers), __runInitializers(this, _restoreStateInterpolationFactor_initializers, 0));
this._currentInterpolationFactor = (__runInitializers(this, _restoreStateInterpolationFactor_extraInitializers), 0);
/** @internal */
this._viewMatrix = new Matrix();
this._useCtrlForPanningInternal = true;
this._panningMouseButtonInternal = 2;
/**
* Defines the allowed panning axis.
*/
this.panningAxis = new Vector3(1, 1, 0);
this._transformedDirection = new Vector3();
/**
* Defines if camera will eliminate transform on y axis.
*/
this.mapPanning = false;
// This is redundant with all _goal* properties being NaN, but we track it anyway because we check for active interpolation in the hot path.
this._isInterpolating = false;
/**
* Observable triggered when the transform node target has been changed on the camera.
*/
this.onMeshTargetChangedObservable = new Observable();
/**
* Defines whether the camera should check collision with the objects oh the scene.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_collisions#how-can-i-do-this-
*/
this.checkCollisions = false;
/**
* Defines the collision radius of the camera.
* This simulates a sphere around the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_collisions#arcrotatecamera
*/
this.collisionRadius = new Vector3(0.5, 0.5, 0.5);
this._previousPosition = Vector3.Zero();
this._collisionVelocity = Vector3.Zero();
this._newPosition = Vector3.Zero();
this._computationVector = Vector3.Zero();
this._goalAlpha = NaN;
this._goalBeta = NaN;
this._goalRadius = NaN;
this._goalTarget = new Vector3(NaN, NaN, NaN);
this._goalTargetScreenOffset = new Vector2(NaN, NaN);
this._onCollisionPositionChange = (collisionId, newPosition, collidedMesh = null) => {
if (!collidedMesh) {
this._previousPosition.copyFrom(this._position);
}
else {
this.setPosition(newPosition);
if (this.onCollide) {
this.onCollide(collidedMesh);
}
}
// Recompute because of constraints
const cosa = Math.cos(this.alpha);
const sina = Math.sin(this.alpha);
const cosb = Math.cos(this.beta);
let sinb = Math.sin(this.beta);
if (sinb === 0) {
sinb = 0.0001;
}
const target = this._getTargetPosition();
this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
target.addToRef(this._computationVector, this._newPosition);
this._position.copyFrom(this._newPosition);
let up = this.upVector;
if (this.allowUpsideDown && this.beta < 0) {
up = up.clone();
up = up.negate();
}
this._computeViewMatrix(this._position, target, up);
this._viewMatrix.addAtIndex(12, this.targetScreenOffset.x);
this._viewMatrix.addAtIndex(13, this.targetScreenOffset.y);
this._collisionTriggered = false;
};
this._target = Vector3.Zero();
if (target) {
this.setTarget(target);
}
this.alpha = alpha;
this.beta = beta;
this.radius = radius;
this.getViewMatrix();
this.inputs = new ArcRotateCameraInputsManager(this);
this.inputs.addKeyboard().addMouseWheel().addPointers();
this.movement = new ArcRotateCameraMovement(this.getScene(), this._position);
// Seed movement-system inertia from the values set during base/subclass construction.
// After this point, the inertia/panningInertia setters on this class push directly to movement.
this.movement.rotationInertia = this._inertia;
this.movement.zoomInertia = this._inertia;
this.movement.panInertia = this._panningInertia;
}
// Cache
/** @internal */
_initCache() {
super._initCache();
this._cache._target = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
this._cache.alpha = undefined;
this._cache.beta = undefined;
this._cache.radius = undefined;
this._cache.targetScreenOffset = Vector2.Zero();
}
/**
* @internal
*/
_updateCache(ignoreParentClass) {
if (!ignoreParentClass) {
super._updateCache();
}
this._cache._target.copyFrom(this._getTargetPosition());
this._cache.alpha = this.alpha;
this._cache.beta = this.beta;
this._cache.radius = this.radius;
this._cache.targetScreenOffset.copyFrom(this.targetScreenOffset);
}
_getTargetPosition() {
if (this._targetHost && this._targetHost.getAbsolutePosition) {
const pos = this._targetHost.getAbsolutePosition();
if (this._targetBoundingCenter) {
pos.addToRef(this._targetBoundingCenter, this._target);
}
else {
this._target.copyFrom(pos);
}
}
const lockedTargetPosition = this._getLockedTargetPosition();
if (lockedTargetPosition) {
return lockedTargetPosition;
}
return this._target;
}
/**
* Stores the current state of the camera (alpha, beta, radius and target)
* @returns the camera itself
*/
storeState() {
this._storedAlpha = this.alpha;
this._storedBeta = this.beta;
this._storedRadius = this.radius;
this._storedTarget = this._getTargetPosition().clone();
this._storedTargetScreenOffset = this.targetScreenOffset.clone();
return super.storeState();
}
/**
* @internal
* Restored camera state. You must call storeState() first
*/
_restoreStateValues() {
if (this.hasStateStored() && this.restoreStateInterpolationFactor > Epsilon && this.restoreStateInterpolationFactor < 1) {
this.interpolateTo(this._storedAlpha, this._storedBeta, this._storedRadius, this._storedTarget, this._storedTargetScreenOffset, this.restoreStateInterpolationFactor);
return true;
}
if (!super._restoreStateValues()) {
return false;
}
this.setTarget(this._storedTarget.clone());
this.alpha = this._storedAlpha;
this.beta = this._storedBeta;
this.radius = this._storedRadius;
this.targetScreenOffset = this._storedTargetScreenOffset.clone();
this.inertialAlphaOffset = 0;
this.inertialBetaOffset = 0;
this.inertialRadiusOffset = 0;
this.inertialPanningX = 0;
this.inertialPanningY = 0;
return true;
}
/**
* Stops any in-progress interpolation.
*/
stopInterpolation() {
this._goalAlpha = NaN;
this._goalBeta = NaN;
this._goalRadius = NaN;
this._goalTarget.set(NaN, NaN, NaN);
this._goalTargetScreenOffset.set(NaN, NaN);
}
/**
* Interpolates the camera to a goal state.
* @param alpha Defines the goal alpha.
* @param beta Defines the goal beta.
* @param radius Defines the goal radius.
* @param target Defines the goal target.
* @param targetScreenOffset Defines the goal target screen offset.
* @param interpolationFactor A value between 0 and 1 that determines the speed of the interpolation.
* @remarks Passing undefined for any of the parameters will use the current value (effectively stopping any in-progress interpolation for that parameter).
* Passing NaN will not start or stop any interpolation for that parameter (effectively allowing multiple interpolations of different parameters to overlap).
*/
interpolateTo(alpha = this.alpha, beta = this.beta, radius = this.radius, target = this.target, targetScreenOffset = this.targetScreenOffset, interpolationFactor) {
this.inertialAlphaOffset = 0;
this.inertialBetaOffset = 0;
this.inertialRadiusOffset = 0;
this.inertialPanningX = 0;
this.inertialPanningY = 0;
if (interpolationFactor != null) {
this._currentInterpolationFactor = interpolationFactor;
}
else if (this.restoreStateInterpolationFactor !== 0) {
this._currentInterpolationFactor = this.restoreStateInterpolationFactor;
}
else {
this._currentInterpolationFactor = 0.1;
}
// If NaN is passed in for a goal value, keep the current goal value.
this._goalAlpha = CheckNaN(alpha, this._goalAlpha);
this._goalBeta = CheckNaN(beta, this._goalBeta);
this._goalRadius = CheckNaN(radius, this._goalRadius);
this._goalTarget.set(CheckNaN(target.x, this._goalTarget.x), CheckNaN(target.y, this._goalTarget.y), CheckNaN(target.z, this._goalTarget.z));
this._goalTargetScreenOffset.set(CheckNaN(targetScreenOffset.x, this._goalTargetScreenOffset.x), CheckNaN(targetScreenOffset.y, this._goalTargetScreenOffset.y));
this._goalAlpha = Clamp(this._goalAlpha, this.lowerAlphaLimit ?? -Infinity, this.upperAlphaLimit ?? Infinity);
this._goalBeta = Clamp(this._goalBeta, this.lowerBetaLimit ?? -Infinity, this.upperBetaLimit ?? Infinity);
this._goalRadius = Clamp(this._goalRadius, this.lowerRadiusLimit ?? -Infinity, this.upperRadiusLimit ?? Infinity);
this._goalTarget.y = Clamp(this._goalTarget.y, this.lowerTargetYLimit ?? -Infinity, Infinity);
this._isInterpolating = true;
}
// Synchronized
/** @internal */
_isSynchronizedViewMatrix() {