UNPKG

spatial-controls

Version:

Configurable 3D movement controls.

1,915 lines (1,889 loc) 79.6 kB
/** * spatial-controls v6.2.1 build Thu Aug 07 2025 * https://github.com/vanruesc/spatial-controls * Copyright 2017 Raoul van Rüschen * @license Zlib */ // src/core/Action.ts var Action = /* @__PURE__ */ ((Action2) => { Action2[Action2["MOVE_FORWARD"] = 0] = "MOVE_FORWARD"; Action2[Action2["MOVE_LEFT"] = 1] = "MOVE_LEFT"; Action2[Action2["MOVE_BACKWARD"] = 2] = "MOVE_BACKWARD"; Action2[Action2["MOVE_RIGHT"] = 3] = "MOVE_RIGHT"; Action2[Action2["MOVE_DOWN"] = 4] = "MOVE_DOWN"; Action2[Action2["MOVE_UP"] = 5] = "MOVE_UP"; Action2[Action2["ZOOM_OUT"] = 6] = "ZOOM_OUT"; Action2[Action2["ZOOM_IN"] = 7] = "ZOOM_IN"; Action2[Action2["BOOST"] = 8] = "BOOST"; Action2[Action2["ROTATE"] = 9] = "ROTATE"; return Action2; })(Action || {}); // src/core/ControlMode.ts var ControlMode = /* @__PURE__ */ ((ControlMode2) => { ControlMode2["FIRST_PERSON"] = "first-person"; ControlMode2["THIRD_PERSON"] = "third-person"; return ControlMode2; })(ControlMode || {}); // src/core/Direction.ts var Direction = /* @__PURE__ */ ((Direction2) => { Direction2[Direction2["FORWARD"] = 0] = "FORWARD"; Direction2[Direction2["LEFT"] = 1] = "LEFT"; Direction2[Direction2["BACKWARD"] = 2] = "BACKWARD"; Direction2[Direction2["RIGHT"] = 3] = "RIGHT"; Direction2[Direction2["DOWN"] = 4] = "DOWN"; Direction2[Direction2["UP"] = 5] = "UP"; return Direction2; })(Direction || {}); // src/core/RotationControls.ts import { EventDispatcher as EventDispatcher8, Quaternion as Quaternion2, Vector2, Vector3 as Vector35 } from "three"; // src/input/PointerBehaviour.ts var PointerBehaviour = /* @__PURE__ */ ((PointerBehaviour2) => { PointerBehaviour2["DEFAULT"] = "default"; PointerBehaviour2["LOCK"] = "lock"; PointerBehaviour2["LOCK_HOLD"] = "lock-hold"; return PointerBehaviour2; })(PointerBehaviour || {}); // src/strategies/RotationStrategy.ts var RotationStrategy = class { /** * The controls. */ controls; /** * Constructs a new rotation strategy. * * @param controls - The controls. */ constructor(controls) { this.controls = controls; } execute(flag, event) { const behaviour = this.controls.settings.pointer.behaviour; const isMouse = event.type === "mousedown" || event.type === "mouseup"; if (isMouse && behaviour !== "default" /* DEFAULT */) { this.controls.setPointerLocked(); } else { this.controls.setRotationEnabled(flag); } } }; // src/strategies/ZoomStrategy.ts var ZoomStrategy = class { /** * A rotation manager. */ rotationManager; /** * Indicates whether this zoom strategy should zoom in. */ zoomIn; /** * Constructs a new zoom strategy. * * @param rotationManager - A rotation manager. * @param zoomIn - Whether this strategy should zoom in. */ constructor(rotationManager, zoomIn) { this.rotationManager = rotationManager; this.zoomIn = zoomIn; } execute(flag) { if (flag) { this.rotationManager.zoom(this.zoomIn ? -1 : 1); } } }; // src/input/KeyCode.ts var KeyCode = /* @__PURE__ */ ((KeyCode2) => { KeyCode2["BACKSPACE"] = "Backspace"; KeyCode2["TAB"] = "Tab"; KeyCode2["ENTER"] = "Enter"; KeyCode2["SHIFT_LEFT"] = "ShiftLeft"; KeyCode2["SHIFT_RIGHT"] = "ShiftRight"; KeyCode2["CTRL_LEFT"] = "ControlLeft"; KeyCode2["CTRL_RIGHT"] = "ControlRight"; KeyCode2["ALT_LEFT"] = "AltLeft"; KeyCode2["ALT_RIGHT"] = "AltRight"; KeyCode2["PAUSE"] = "Pause"; KeyCode2["CAPS_LOCK"] = "CapsLock"; KeyCode2["ESCAPE"] = "Escape"; KeyCode2["SPACE"] = "Space"; KeyCode2["PAGE_UP"] = "PageUp"; KeyCode2["PAGE_DOWN"] = "PageDown"; KeyCode2["END"] = "End"; KeyCode2["HOME"] = "Home"; KeyCode2["ARROW_LEFT"] = "ArrowLeft"; KeyCode2["ARROW_UP"] = "ArrowUp"; KeyCode2["ARROW_RIGHT"] = "ArrowRight"; KeyCode2["ARROW_DOWN"] = "ArrowDown"; KeyCode2["INSERT"] = "Insert"; KeyCode2["DELETE"] = "Delete"; KeyCode2["DIGIT_0"] = "Digit0"; KeyCode2["DIGIT_1"] = "Digit1"; KeyCode2["DIGIT_2"] = "Digit2"; KeyCode2["DIGIT_3"] = "Digit3"; KeyCode2["DIGIT_4"] = "Digit4"; KeyCode2["DIGIT_5"] = "Digit5"; KeyCode2["DIGIT_6"] = "Digit6"; KeyCode2["DIGIT_7"] = "Digit7"; KeyCode2["DIGIT_8"] = "Digit8"; KeyCode2["DIGIT_9"] = "Digit9"; KeyCode2["KEY_A"] = "KeyA"; KeyCode2["KEY_B"] = "KeyB"; KeyCode2["KEY_C"] = "KeyC"; KeyCode2["KEY_D"] = "KeyD"; KeyCode2["KEY_E"] = "KeyE"; KeyCode2["KEY_F"] = "KeyF"; KeyCode2["KEY_G"] = "KeyG"; KeyCode2["KEY_H"] = "KeyH"; KeyCode2["KEY_I"] = "KeyI"; KeyCode2["KEY_J"] = "KeyJ"; KeyCode2["KEY_K"] = "KeyK"; KeyCode2["KEY_L"] = "KeyL"; KeyCode2["KEY_M"] = "KeyM"; KeyCode2["KEY_N"] = "KeyN"; KeyCode2["KEY_O"] = "KeyO"; KeyCode2["KEY_P"] = "KeyP"; KeyCode2["KEY_Q"] = "KeyQ"; KeyCode2["KEY_R"] = "KeyR"; KeyCode2["KEY_S"] = "KeyS"; KeyCode2["KEY_T"] = "KeyT"; KeyCode2["KEY_U"] = "KeyU"; KeyCode2["KEY_V"] = "KeyV"; KeyCode2["KEY_W"] = "KeyW"; KeyCode2["KEY_X"] = "KeyX"; KeyCode2["KEY_Y"] = "KeyY"; KeyCode2["KEY_Z"] = "KeyZ"; KeyCode2["OS_LEFT"] = "OSLeft"; KeyCode2["OS_RIGHT"] = "OSRight"; KeyCode2["META_LEFT"] = "MetaLeft"; KeyCode2["META_RIGHT"] = "MetaRight"; KeyCode2["MEDIA_SELECT"] = "MediaSelect"; KeyCode2["NUMPAD_0"] = "Numpad0"; KeyCode2["NUMPAD_1"] = "Numpad1"; KeyCode2["NUMPAD_2"] = "Numpad2"; KeyCode2["NUMPAD_3"] = "Numpad3"; KeyCode2["NUMPAD_4"] = "Numpad4"; KeyCode2["NUMPAD_5"] = "Numpad5"; KeyCode2["NUMPAD_6"] = "Numpad6"; KeyCode2["NUMPAD_7"] = "Numpad7"; KeyCode2["NUMPAD_8"] = "Numpad8"; KeyCode2["NUMPAD_9"] = "Numpad9"; KeyCode2["NUMPAD_MULTIPLY"] = "NumpadMultiply"; KeyCode2["NUMPAD_ADD"] = "NumpadAdd"; KeyCode2["NUMPAD_SUBTRACT"] = "NumpadSubtract"; KeyCode2["NUMPAD_DECIMAL"] = "NumpadDecimal"; KeyCode2["NUMPAD_DIVIDE"] = "NumpadDivide"; KeyCode2["F1"] = "F1"; KeyCode2["F2"] = "F2"; KeyCode2["F3"] = "F3"; KeyCode2["F4"] = "F4"; KeyCode2["F5"] = "F5"; KeyCode2["F6"] = "F6"; KeyCode2["F7"] = "F7"; KeyCode2["F8"] = "F8"; KeyCode2["F9"] = "F9"; KeyCode2["F10"] = "F10"; KeyCode2["F11"] = "F11"; KeyCode2["F12"] = "F12"; KeyCode2["NUM_LOCK"] = "NumLock"; KeyCode2["SCROLL_LOCK"] = "ScrollLock"; KeyCode2["SEMICOLON"] = "Semicolon"; KeyCode2["EQUAL"] = "Equal"; KeyCode2["COMMA"] = "Comma"; KeyCode2["MINUS"] = "Minus"; KeyCode2["PERIOD"] = "Period"; KeyCode2["SLASH"] = "Slash"; KeyCode2["BACKQUOTE"] = "Backquote"; KeyCode2["BRACKET_LEFT"] = "BracketLeft"; KeyCode2["BRACKET_RIGHT"] = "BracketRight"; KeyCode2["BACKSLASH"] = "Backslash"; return KeyCode2; })(KeyCode || {}); // src/input/keyCodeLegacy.ts var keyCodeLegacy = /* @__PURE__ */ new Map([ [8, "Backspace" /* BACKSPACE */], [9, "Tab" /* TAB */], [13, "Enter" /* ENTER */], [16, "ShiftLeft" /* SHIFT_LEFT */], [17, "ControlLeft" /* CTRL_LEFT */], [18, "AltLeft" /* ALT_LEFT */], [19, "Pause" /* PAUSE */], [20, "CapsLock" /* CAPS_LOCK */], [27, "Escape" /* ESCAPE */], [32, "Space" /* SPACE */], [33, "PageUp" /* PAGE_UP */], [34, "PageDown" /* PAGE_DOWN */], [35, "End" /* END */], [36, "Home" /* HOME */], [37, "ArrowLeft" /* ARROW_LEFT */], [38, "ArrowUp" /* ARROW_UP */], [39, "ArrowRight" /* ARROW_RIGHT */], [40, "ArrowDown" /* ARROW_DOWN */], [45, "Insert" /* INSERT */], [46, "Delete" /* DELETE */], [48, "Digit0" /* DIGIT_0 */], [49, "Digit1" /* DIGIT_1 */], [50, "Digit2" /* DIGIT_2 */], [51, "Digit3" /* DIGIT_3 */], [52, "Digit4" /* DIGIT_4 */], [53, "Digit5" /* DIGIT_5 */], [54, "Digit6" /* DIGIT_6 */], [55, "Digit7" /* DIGIT_7 */], [56, "Digit8" /* DIGIT_8 */], [57, "Digit9" /* DIGIT_9 */], [65, "KeyA" /* KEY_A */], [66, "KeyB" /* KEY_B */], [67, "KeyC" /* KEY_C */], [68, "KeyD" /* KEY_D */], [69, "KeyE" /* KEY_E */], [70, "KeyF" /* KEY_F */], [71, "KeyG" /* KEY_G */], [72, "KeyH" /* KEY_H */], [73, "KeyI" /* KEY_I */], [74, "KeyJ" /* KEY_J */], [75, "KeyK" /* KEY_K */], [76, "KeyL" /* KEY_L */], [77, "KeyM" /* KEY_M */], [78, "KeyN" /* KEY_N */], [79, "KeyO" /* KEY_O */], [80, "KeyP" /* KEY_P */], [81, "KeyQ" /* KEY_Q */], [82, "KeyR" /* KEY_R */], [83, "KeyS" /* KEY_S */], [84, "KeyT" /* KEY_T */], [85, "KeyU" /* KEY_U */], [86, "KeyV" /* KEY_V */], [87, "KeyW" /* KEY_W */], [88, "KeyX" /* KEY_X */], [89, "KeyY" /* KEY_Y */], [90, "KeyZ" /* KEY_Z */], [91, "MetaLeft" /* META_LEFT */], [92, "MetaRight" /* META_RIGHT */], [93, "MediaSelect" /* MEDIA_SELECT */], [96, "Numpad0" /* NUMPAD_0 */], [97, "Numpad1" /* NUMPAD_1 */], [98, "Numpad2" /* NUMPAD_2 */], [99, "Numpad3" /* NUMPAD_3 */], [100, "Numpad4" /* NUMPAD_4 */], [101, "Numpad5" /* NUMPAD_5 */], [102, "Numpad6" /* NUMPAD_6 */], [103, "Numpad7" /* NUMPAD_7 */], [104, "Numpad8" /* NUMPAD_8 */], [105, "Numpad9" /* NUMPAD_9 */], [106, "NumpadMultiply" /* NUMPAD_MULTIPLY */], [107, "NumpadAdd" /* NUMPAD_ADD */], [109, "NumpadSubtract" /* NUMPAD_SUBTRACT */], [110, "NumpadDecimal" /* NUMPAD_DECIMAL */], [111, "NumpadDivide" /* NUMPAD_DIVIDE */], [112, "F1" /* F1 */], [113, "F2" /* F2 */], [114, "F3" /* F3 */], [115, "F4" /* F4 */], [116, "F5" /* F5 */], [117, "F6" /* F6 */], [118, "F7" /* F7 */], [119, "F8" /* F8 */], [120, "F9" /* F9 */], [121, "F10" /* F10 */], [122, "F11" /* F11 */], [123, "F12" /* F12 */], [144, "NumLock" /* NUM_LOCK */], [145, "ScrollLock" /* SCROLL_LOCK */], [186, "Semicolon" /* SEMICOLON */], [187, "Equal" /* EQUAL */], [188, "Comma" /* COMMA */], [189, "Minus" /* MINUS */], [190, "Period" /* PERIOD */], [191, "Slash" /* SLASH */], [192, "Backquote" /* BACKQUOTE */], [219, "BracketLeft" /* BRACKET_LEFT */], [221, "BracketRight" /* BRACKET_RIGHT */], [220, "Backslash" /* BACKSLASH */] ]); // src/input/PointerType.ts var PointerType = /* @__PURE__ */ ((PointerType2) => { PointerType2["MOUSE"] = "mouse"; PointerType2["PEN"] = "pen"; PointerType2["TOUCH"] = "touch"; return PointerType2; })(PointerType || {}); // src/managers/RotationManager.ts import { EventDispatcher, Matrix4, Spherical, Vector3 } from "three"; // src/core/time.ts var MILLISECONDS_TO_SECONDS = 1 / 1e3; // src/math/ScalarDamper.ts var ScalarDamper = class { /** * The maximum speed. */ maxSpeed; /** * The current velocity. */ velocity; /** * Constructs a new scalar damper. * * @param maxSpeed - The maximum speed at which the value can change. */ constructor(maxSpeed = Number.POSITIVE_INFINITY) { this.maxSpeed = maxSpeed; this.velocity = 0; } /** * Resets the velocity. */ resetVelocity() { this.velocity = 0; } /** * Smooth interpolation with exponential velocity gain/decay. * * @param a - The start value. * @param b - The target value. * @param lambda - A smoothing factor. * @param omega - See {@link ScalarDamper.calculateOmega}. * @param exp - See {@link ScalarDamper.calculateExp}. * @param dt - The delta time in seconds. * @return The interpolated value. */ interpolate(a, b, lambda, omega, exp, dt) { const maxChange = this.maxSpeed * Math.max(lambda, 1e-4); const change = Math.min(Math.max(a - b, -maxChange), maxChange); const c = a - change; const velocity = this.velocity; const t = (velocity + omega * change) * dt; this.velocity = (velocity - omega * t) * exp; let result = c + (change + t) * exp; if (Math.abs(change) < 1e-6) { result = b; this.velocity = 0; } else if (b - a > 0 === result > b) { this.velocity = (result - b) / dt; result = b; } return result; } /** * Calculates the Omega coefficient which can be reused for interpolations during the same frame. * * @param lambda - A smoothing factor. * @return Omega. */ static calculateOmega(lambda) { return 2 / Math.max(lambda, 1e-4); } /** * Calculates the exponentional factor which can be reused for interpolations during the same frame. * * @param omega - See {@link ScalarDamper.calculateOmega}. * @param dt - The delta time in seconds. * @return The exponentional interpolation factor. */ static calculateExp(omega, dt) { const x2 = omega * dt; const x22 = x2 * x2; return 1 / (1 + x2 + 0.48 * x22 + 0.235 * x2 * x22); } }; // src/managers/RotationManager.ts var TWO_PI = 2 * Math.PI; var u = /* @__PURE__ */ new Vector3(); var v = /* @__PURE__ */ new Vector3(); var m = /* @__PURE__ */ new Matrix4(); var RotationManager = class _RotationManager extends EventDispatcher { /** * Triggers when the position or quaternion is changed. * * @event */ static EVENT_UPDATE = "update"; /** * @see {@link position} */ _position; /** * @see {@link quaternion} */ _quaternion; /** * @see {@link target} */ _target; /** * The settings. */ settings; /** * The current spherical coordinates. */ spherical0; /** * The spherical target coordinates. */ spherical1; /** * Scalar dampers. */ scalarDampers; /** * A timestamp. */ timestamp; /** * A reusable update event. */ updateEvent; /** * Constructs a new rotation manager. * * @param position - The position. * @param quaternion - The quaternion. * @param target - The target. * @param settings - The settings. */ constructor(position, quaternion, target, settings) { super(); this._position = position; this._quaternion = quaternion; this._target = target; this.settings = settings; this.spherical0 = new Spherical(); this.spherical1 = new Spherical(); this.timestamp = 0; this.updateEvent = { type: _RotationManager.EVENT_UPDATE }; this.scalarDampers = Object.freeze([ new ScalarDamper(), new ScalarDamper(), new ScalarDamper() ]); } /** * The position. */ get position() { return this._position; } set position(value) { this._position = value; } /** * The quaternion. */ get quaternion() { return this._quaternion; } set quaternion(value) { this._quaternion = value; } /** * The target. */ get target() { return this._target; } set target(value) { this._target = value; } /** * The current radius. */ get radius() { return this.spherical0.radius; } /** * Resets the current velocity. */ resetVelocity() { this.spherical1.copy(this.spherical0); for (const scalarDamper of this.scalarDampers) { scalarDamper.resetVelocity(); } } /** * Restricts the spherical angles. * * @return This manager. */ restrictAngles() { const s = this.spherical1; const rotation = this.settings.rotation; const thetaMin = rotation.minAzimuthalAngle; const thetaMax = rotation.maxAzimuthalAngle; const phiMin = rotation.minPolarAngle; const phiMax = rotation.maxPolarAngle; s.theta = Math.min(Math.max(s.theta, thetaMin), thetaMax); s.phi = Math.min(Math.max(s.phi, phiMin), phiMax); if (s.phi === 0 || s.phi === Math.PI) { s.makeSafe(); } return this; } /** * Restricts the spherical radius. * * @return This manager. */ restrictRadius() { const s = this.spherical1; const zoom = this.settings.zoom; const min = zoom.minDistance; const max = zoom.maxDistance; s.radius = Math.min(Math.max(s.radius, min), max); return this; } /** * Restricts the spherical system. * * @return This manager. */ restrictSpherical() { return this.restrictRadius().restrictAngles(); } /** * Updates the spherical coordinates based on the position and target. * * @return This manager. */ updateSpherical() { if (this.settings.general.mode === "third-person" /* THIRD_PERSON */) { const pivotOffset = this.settings.rotation.pivotOffset; v.subVectors(u.subVectors(this.position, pivotOffset), this.target); this.spherical1.setFromVector3(v); } else { this.spherical1.setFromVector3(this.target); } this.restrictSpherical(); this.spherical0.copy(this.spherical1); return this; } /** * Updates the position based on the spherical coordinates. * * @return This manager. */ updatePosition() { if (this.settings.general.mode === "third-person" /* THIRD_PERSON */) { const pivotOffset = this.settings.rotation.pivotOffset; this.position.setFromSpherical(this.spherical0).add(this.target).add(pivotOffset); } return this; } /** * Updates the quaternion. * * @return This manager. */ updateQuaternion() { const settings = this.settings; const rotation = settings.rotation; const target = this.target; const up = u.copy(rotation.up); const phi = this.spherical0.phi % TWO_PI; if (phi < 0 && phi > -Math.PI || phi > Math.PI && phi < TWO_PI) { up.negate(); } if (settings.general.mode === "third-person" /* THIRD_PERSON */) { m.lookAt(v.subVectors(this.position, target), rotation.pivotOffset, up); } else { m.lookAt(v.set(0, 0, 0), target.setFromSpherical(this.spherical0), up); } this.quaternion.setFromRotationMatrix(m); this.dispatchEvent(this.updateEvent); return this; } /** * Adjusts the spherical system. * * @param theta - The angle to add to theta in radians. * @param phi - The angle to add to phi in radians. * @return This manager. */ adjustSpherical(theta, phi) { const s = this.spherical1; const settings = this.settings; const rotation = settings.rotation; const invertedY = rotation.invertedY; const orbit = settings.general.mode === "third-person" /* THIRD_PERSON */; const orbitXorInvertedY = (orbit || invertedY) && !(orbit && invertedY); s.theta = rotation.invertedX ? s.theta + theta : s.theta - theta; s.phi = orbitXorInvertedY ? s.phi - phi : s.phi + phi; return this.restrictAngles(); } /** * Zooms in or out. Only applies in third person mode. * * @param sign - The zoom sign. Possible values are [-1, 0, 1]. * @return This manager. */ zoom(sign) { const s = this.spherical1; const settings = this.settings; const zoom = settings.zoom; if (zoom.enabled && settings.general.mode === "third-person" /* THIRD_PERSON */) { const amount = sign * zoom.sensitivity; s.radius = zoom.inverted ? s.radius - amount : s.radius + amount; this.restrictRadius(); } return this; } /** * Looks at the given point. * * @param point - The target point. * @return This manager. */ lookAt(point) { if (this.settings.general.mode === "third-person" /* THIRD_PERSON */) { this.target.copy(point).sub(this.settings.rotation.pivotOffset); } else { this.target.subVectors(point, this.position).normalize(); } return this; } /** * Returns the current view direction. * * @param view - A vector to store the direction in. * @return The normalized view direction. */ getViewDirection(view) { const orbit = this.settings.general.mode === "third-person" /* THIRD_PERSON */; view.setFromSpherical(this.spherical0).normalize(); return orbit ? view.negate() : view; } /** * Returns the projected view direction. * * The projected direction will be reached if there are no further rotation adjustments. If damping is disabled, * the vector will be equal to the direction returned by {@link getViewDirection}. * * @param view - A vector to store the direction in. * @return The normalized view direction. */ getProjectedViewDirection(view) { const orbit = this.settings.general.mode === "third-person" /* THIRD_PERSON */; view.setFromSpherical(this.spherical1).normalize(); return orbit ? view.negate() : view; } update(timestamp) { const s0 = this.spherical0; const s1 = this.spherical1; const equal = s0.radius === s1.radius && s0.theta === s1.theta && s0.phi === s1.phi; if (!equal) { const settings = this.settings; const scalarDampers = this.scalarDampers; const elapsed = (timestamp - this.timestamp) * MILLISECONDS_TO_SECONDS; if (settings.rotation.damping > 0) { const damping = settings.rotation.damping; const omega = ScalarDamper.calculateOmega(damping); const exp = ScalarDamper.calculateExp(omega, elapsed); s0.theta = scalarDampers[0].interpolate(s0.theta, s1.theta, damping, omega, exp, elapsed); s0.phi = scalarDampers[1].interpolate(s0.phi, s1.phi, damping, omega, exp, elapsed); } else { s0.theta = s1.theta; s0.phi = s1.phi; } if (settings.zoom.damping > 0) { const damping = settings.zoom.damping; const omega = ScalarDamper.calculateOmega(damping); const exp = ScalarDamper.calculateExp(omega, elapsed); s0.radius = scalarDampers[2].interpolate(s0.radius, s1.radius, damping, omega, exp, elapsed); } else { s0.radius = s1.radius; } this.updatePosition().updateQuaternion(); } else { if (Math.abs(s0.theta) >= TWO_PI) { s0.theta %= TWO_PI; s1.theta %= TWO_PI; } if (Math.abs(s0.phi) >= TWO_PI) { s0.phi %= TWO_PI; s1.phi %= TWO_PI; } } this.timestamp = timestamp; } }; // src/settings/Settings.ts import { EventDispatcher as EventDispatcher7 } from "three"; // src/input/PointerButton.ts var PointerButton = /* @__PURE__ */ ((PointerButton2) => { PointerButton2[PointerButton2["MAIN"] = 0] = "MAIN"; PointerButton2[PointerButton2["AUXILIARY"] = 1] = "AUXILIARY"; PointerButton2[PointerButton2["SECONDARY"] = 2] = "SECONDARY"; return PointerButton2; })(PointerButton || {}); // src/settings/Bindings.ts var Bindings = class _Bindings { /** * The default bindings. */ defaultActions; /** * A collection that maps keys to actions. */ actions; /** * Constructs new input bindings. */ constructor() { this.defaultActions = /* @__PURE__ */ new Map(); this.actions = /* @__PURE__ */ new Map(); } /** * Resets the current bindings to match the default bindings. * * @return This instance. */ reset() { this.actions = new Map(this.defaultActions); return this; } /** * Establishes default bindings and resets the current bindings. * * @param actions - A collection that maps keys to actions. * @return This instance. */ setDefault(actions) { this.defaultActions = actions; return this.reset(); } /** * Clears the default bindings. * * @return This instance. */ clearDefault() { this.defaultActions.clear(); return this; } /** * Clears the current bindings. * * @return This instance. */ clear() { this.actions.clear(); return this; } /** * Copies the given bindings, including the default bindings. * * @param bindings - Bindings. * @return This instance. */ copy(bindings) { this.defaultActions = new Map(bindings.defaultActions); this.actions = new Map(bindings.actions); return this; } /** * Clones these bindings. * * @return The cloned bindings. */ clone() { const clone = new _Bindings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { if (json !== void 0) { this.defaultActions = new Map(json.defaultActions); this.actions = new Map(json.actions); } return this; } /** * Checks if the given key is bound to an action. * * @param key - A key. * @return Whether the given key is bound to an action. */ has(key) { return this.actions.has(key); } /** * Returns the action that is bound to the given key. * * @param key - A key. * @return The action, or undefined if the key is not bound to any action. */ get(key) { return this.actions.get(key); } /** * Binds a key to an action. * * @param key - A key. * @param action - An action. * @return This instance. */ set(key, action) { this.actions.set(key, action); return this; } /** * Unbinds a key. * * @param key - The key. * @return Whether the binding existed. */ delete(key) { return this.actions.delete(key); } toJSON() { return { defaultActions: [...this.defaultActions], actions: [...this.actions] }; } }; // src/settings/GeneralSettings.ts import { EventDispatcher as EventDispatcher2 } from "three"; var GeneralSettings = class _GeneralSettings extends EventDispatcher2 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; // #region Backing Data /** * @see {@link mode} */ _mode; /** * @see {@link previousMode} */ _previousMode; // #endegion /** * Constructs new general settings. */ constructor() { super(); this._mode = "first-person" /* FIRST_PERSON */; this._previousMode = this._mode; } /** * The previous control mode. * * @internal */ get previousMode() { return this._previousMode; } /** * The control mode. */ get mode() { return this._mode; } set mode(value) { if (this._mode !== value) { this._mode = value; this.dispatchEvent({ type: _GeneralSettings.EVENT_CHANGE }); this._previousMode = value; } } /** * Copies the given general settings. * * @param settings - General settings. * @return This instance. */ copy(settings) { this.mode = settings.mode; return this; } /** * Clones this general settings instance. * * @return The cloned general settings. */ clone() { const clone = new _GeneralSettings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { this.mode = json.mode; return this; } toJSON() { return { mode: this.mode }; } }; // src/settings/PointerSettings.ts import { EventDispatcher as EventDispatcher3 } from "three"; var PointerSettings = class _PointerSettings extends EventDispatcher3 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; // #region Backing Data /** * @see {@link behaviour} */ _behaviour; /** * @see {@link sensitivity} */ _sensitivity; // #endregion /** * Constructs new pointer settings. */ constructor() { super(); this._behaviour = "default" /* DEFAULT */; this._sensitivity = 1e-3; } /** * The pointer behaviour. */ get behaviour() { return this._behaviour; } set behaviour(value) { this._behaviour = value; this.dispatchEvent({ type: _PointerSettings.EVENT_CHANGE }); } /** * Sets the sensitivity. * * This sensitivity acts as a baseline scale for pointer movement deltas. Default is `1e-3`. */ get sensitivity() { return this._sensitivity; } set sensitivity(value) { this._sensitivity = value; this.dispatchEvent({ type: _PointerSettings.EVENT_CHANGE }); } /** * Copies the given pointer settings. * * @param settings - Pointer settings. * @return This instance. */ copy(settings) { this.behaviour = settings.behaviour; this.sensitivity = settings.sensitivity; return this; } /** * Clones this pointer settings instance. * * @return The cloned pointer settings. */ clone() { const clone = new _PointerSettings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { this.behaviour = json.behaviour; this.sensitivity = json.sensitivity; return this; } toJSON() { return { behaviour: this.behaviour, sensitivity: this.sensitivity }; } }; // src/settings/RotationSettings.ts import { EventDispatcher as EventDispatcher4, Vector3 as Vector33 } from "three"; // src/core/axes.ts import { Vector3 as Vector32 } from "three"; var x = new Vector32(1, 0, 0); var y = new Vector32(0, 1, 0); var z = new Vector32(0, 0, 1); // src/settings/RotationSettings.ts var RotationSettings = class _RotationSettings extends EventDispatcher4 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; // #region Backing Data /** * @see {@link enabled} */ _enabled; /** * @see {@link up} */ _up; /** * @see {@link pivotOffset} */ _pivotOffset; /** * @see {@link minAzimuthalAngle} */ _minAzimuthalAngle; /** * @see {@link maxAzimuthalAngle} */ _maxAzimuthalAngle; /** * @see {@link minPolarAngle} */ _minPolarAngle; /** * @see {@link maxPolarAngle} */ _maxPolarAngle; /** * @see {@link invertedX} */ _invertedX; /** * @see {@link invertedY} */ _invertedY; /** * @see {@link sensitivityX} */ _sensitivityX; /** * @see {@link sensitivityY} */ _sensitivityY; /** * @see {@link damping} */ _damping; // #endregion /** * Constructs new rotation settings. */ constructor() { super(); this._enabled = true; this._up = new Vector33(); this._up.copy(y); this._pivotOffset = new Vector33(); this._minAzimuthalAngle = Number.NEGATIVE_INFINITY; this._maxAzimuthalAngle = Number.POSITIVE_INFINITY; this._minPolarAngle = 0; this._maxPolarAngle = Math.PI; this._invertedX = false; this._invertedY = false; this._sensitivityX = 1; this._sensitivityY = 1; this._damping = 0; } /** * Indicates whether rotation is enabled. */ get enabled() { return this._enabled; } set enabled(value) { this._enabled = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * A normalized up vector. */ get up() { return this._up; } set up(value) { this._up = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The pivot offset. */ get pivotOffset() { return this._pivotOffset; } set pivotOffset(value) { this._pivotOffset = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The minimum azimuthal angle in radians. Range: [-Math.PI, Math.PI]. */ get minAzimuthalAngle() { return this._minAzimuthalAngle; } set minAzimuthalAngle(value) { this._minAzimuthalAngle = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The maximum azimuthal angle in radians. Range: [-Math.PI, Math.PI]. */ get maxAzimuthalAngle() { return this._maxAzimuthalAngle; } set maxAzimuthalAngle(value) { this._maxAzimuthalAngle = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The minimum polar angle in radians. Range: [0, Math.PI]. */ get minPolarAngle() { return this._minPolarAngle; } set minPolarAngle(value) { this._minPolarAngle = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The maximum polar angle in radians. Range: [0, Math.PI]. */ get maxPolarAngle() { return this._maxPolarAngle; } set maxPolarAngle(value) { this._maxPolarAngle = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * Indicates whether the horizontal rotation is inverted. */ get invertedX() { return this._invertedX; } set invertedX(value) { this._invertedX = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * Indicates whether the vertical rotation is inverted. */ get invertedY() { return this._invertedY; } set invertedY(value) { this._invertedY = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The horizontal rotation sensitivity. */ get sensitivityX() { return this._sensitivityX; } set sensitivityX(value) { this._sensitivityX = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The vertical rotation sensitivity. */ get sensitivityY() { return this._sensitivityY; } set sensitivityY(value) { this._sensitivityY = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * Sets the horizontal and vertical rotation sensitivity. */ set sensitivity(value) { this._sensitivityX = this._sensitivityY = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * The damping factor. */ get damping() { return this._damping; } set damping(value) { this._damping = value; this.dispatchEvent({ type: _RotationSettings.EVENT_CHANGE }); } /** * Copies the given rotation settings. * * @param settings - Rotation settings. * @return This instance. */ copy(settings) { this.up.copy(settings.up); this.pivotOffset.copy(settings.pivotOffset); this.minAzimuthalAngle = settings.minAzimuthalAngle; this.maxAzimuthalAngle = settings.maxAzimuthalAngle; this.minPolarAngle = settings.minPolarAngle; this.maxPolarAngle = settings.maxPolarAngle; this.invertedX = settings.invertedX; this.invertedY = settings.invertedY; this.sensitivityX = settings.sensitivityX; this.sensitivityY = settings.sensitivityY; this.damping = settings.damping; return this; } /** * Clones this rotation settings instance. * * @return The cloned rotation settings. */ clone() { const clone = new _RotationSettings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { this.enabled = json.enabled; this.up.copy(json.up); this.pivotOffset.copy(json.pivotOffset); this.minAzimuthalAngle = json.minAzimuthalAngle ?? Number.NEGATIVE_INFINITY; this.maxAzimuthalAngle = json.maxAzimuthalAngle ?? Number.POSITIVE_INFINITY; this.minPolarAngle = json.minPolarAngle ?? Number.NEGATIVE_INFINITY; this.maxPolarAngle = json.maxPolarAngle ?? Number.POSITIVE_INFINITY; this.invertedX = json.invertedX; this.invertedY = json.invertedY; this.sensitivityX = json.sensitivityX; this.sensitivityY = json.sensitivityY; this.damping = json.damping; return this; } toJSON() { return { enabled: this.enabled, up: this.up, pivotOffset: this.pivotOffset, minAzimuthalAngle: this.minAzimuthalAngle, maxAzimuthalAngle: this.maxAzimuthalAngle, minPolarAngle: this.minPolarAngle, maxPolarAngle: this.maxPolarAngle, invertedX: this.invertedX, invertedY: this.invertedY, sensitivityX: this.sensitivityX, sensitivityY: this.sensitivityY, damping: this.damping }; } }; // src/settings/TranslationSettings.ts import { EventDispatcher as EventDispatcher5, Vector3 as Vector34 } from "three"; var TranslationSettings = class _TranslationSettings extends EventDispatcher5 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; // #region Backing Data /** * @see {@link enabled} */ _enabled; /** * @see {@link sensitivity} */ _sensitivity; /** * @see {@link boostMultiplier} */ _boostMultiplier; /** * @see {@link axisModifier} */ _axisWeights; /** * @see {@link damping} */ _damping; // #endregion /** * Constructs new translation settings. */ constructor() { super(); this._enabled = true; this._sensitivity = 1; this._boostMultiplier = 2; this._axisWeights = new Vector34(1, 1, 1); this._damping = 0; } /** * Indicates whether positional translation is enabled. */ get enabled() { return this._enabled; } set enabled(value) { this._enabled = value; this.dispatchEvent({ type: _TranslationSettings.EVENT_CHANGE }); } /** * The translation sensitivity. */ get sensitivity() { return this._sensitivity; } set sensitivity(value) { this._sensitivity = value; this.dispatchEvent({ type: _TranslationSettings.EVENT_CHANGE }); } /** * The translation boost multiplier. */ get boostMultiplier() { return this._boostMultiplier; } set boostMultiplier(value) { this._boostMultiplier = Math.max(value, 1); this.dispatchEvent({ type: _TranslationSettings.EVENT_CHANGE }); } /** * Weights that influence movement along each axis. */ get axisWeights() { return this._axisWeights; } set axisWeights(value) { this._axisWeights = value; this.dispatchEvent({ type: _TranslationSettings.EVENT_CHANGE }); } /** * The damping factor. Range is [0.0, +Infinity]. Set to 0 to disable. */ get damping() { return this._damping; } set damping(value) { this._damping = value; this.dispatchEvent({ type: _TranslationSettings.EVENT_CHANGE }); } /** * Copies the given translation settings. * * @param settings - Translation settings. * @return This instance. */ copy(settings) { this.enabled = settings.enabled; this.sensitivity = settings.sensitivity; this.boostMultiplier = settings.boostMultiplier; this.damping = settings.damping; return this; } /** * Clones this translation settings instance. * * @return The cloned translation settings. */ clone() { const clone = new _TranslationSettings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { this.enabled = json.enabled; this.sensitivity = json.sensitivity; this.boostMultiplier = json.boostMultiplier; this.damping = json.damping; if (json.axisWeights !== void 0) { this.axisWeights.copy(json.axisWeights); } return this; } toJSON() { return { enabled: this.enabled, sensitivity: this.sensitivity, boostMultiplier: this.boostMultiplier, axisWeights: this.axisWeights, damping: this.damping }; } }; // src/settings/ZoomSettings.ts import { EventDispatcher as EventDispatcher6 } from "three"; var ZoomSettings = class _ZoomSettings extends EventDispatcher6 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; // #region Backing Data /** * @see {@link enabled} */ _enabled; /** * @see {@link inverted} */ _inverted; /** * @see {@link minDistance} */ _minDistance; /** * @see {@link maxDistance} */ _maxDistance; /** * @see {@link sensitivity} */ _sensitivity; /** * @see {@link damping} */ _damping; // #endregion /** * Constructs new zoom settings. */ constructor() { super(); this._enabled = true; this._inverted = false; this._minDistance = 1e-6; this._maxDistance = Number.POSITIVE_INFINITY; this._sensitivity = 1; this._damping = 0; } /** * Indicates whether zooming is enabled. */ get enabled() { return this._enabled; } set enabled(value) { this._enabled = value; this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * Indicates whether the zoom controls should be inverted. */ get inverted() { return this._inverted; } set inverted(value) { this._inverted = value; this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * The minimum zoom distance. */ get minDistance() { return this._minDistance; } set minDistance(value) { this._minDistance = Math.min( Math.max(value, 1e-6), Number.POSITIVE_INFINITY ); this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * The maximum zoom distance. */ get maxDistance() { return this._maxDistance; } set maxDistance(value) { this._maxDistance = Math.min( Math.max(value, this._minDistance), Number.POSITIVE_INFINITY ); this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * Sets the minimum and maximum zoom distance. * * @param min - The minimum distance. * @param max - The maximum distance. */ setRange(min, max) { this._minDistance = min; this._maxDistance = max; this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * The zoom sensitivity. */ get sensitivity() { return this._sensitivity; } set sensitivity(value) { this._sensitivity = value; this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * The damping factor. */ get damping() { return this._damping; } set damping(value) { this._damping = value; this.dispatchEvent({ type: _ZoomSettings.EVENT_CHANGE }); } /** * Copies the given zoom settings. * * @param settings - Zoom settings. * @return This instance. */ copy(settings) { this.enabled = settings.enabled; this.inverted = settings.inverted; this.minDistance = settings.minDistance; this.maxDistance = settings.maxDistance; this.sensitivity = settings.sensitivity; this.damping = settings.damping; return this; } /** * Clones this zoom settings instance. * * @return The cloned zoom settings. */ clone() { const clone = new _ZoomSettings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data. * @return This instance. */ fromJSON(json) { this.enabled = json.enabled; this.inverted = json.inverted; this.minDistance = json.minDistance; this.maxDistance = json.maxDistance || Number.POSITIVE_INFINITY; this.sensitivity = json.sensitivity; this.damping = json.damping; return this; } toJSON() { return { enabled: this.enabled, inverted: this.inverted, minDistance: this.minDistance, maxDistance: this.maxDistance, sensitivity: this.sensitivity, damping: this.damping }; } }; // src/settings/Settings.ts var Settings = class _Settings extends EventDispatcher7 { /** * Triggers when the settings are changed. * * @event */ static EVENT_CHANGE = "change"; /** * Key bindings. * * This collection maps {@linkplain KeyCode key codes} to {@linkplain Action actions}. */ keyBindings; /** * Pointer bindings. * * This collection maps {@linkplain PointerButton pointer buttons} to {@linkplain Action actions}. */ pointerBindings; /** * General settings. */ general; /** * Pointer settings. */ pointer; /** * Rotation settings. */ rotation; /** * Translation settings. */ translation; /** * Zoom settings. */ zoom; /** * Constructs new settings. */ constructor() { super(); this.keyBindings = new Bindings(); this.keyBindings.setDefault(/* @__PURE__ */ new Map([ ["KeyW" /* KEY_W */, 0 /* MOVE_FORWARD */], ["KeyA" /* KEY_A */, 1 /* MOVE_LEFT */], ["KeyS" /* KEY_S */, 2 /* MOVE_BACKWARD */], ["KeyD" /* KEY_D */, 3 /* MOVE_RIGHT */], ["ArrowUp" /* ARROW_UP */, 0 /* MOVE_FORWARD */], ["ArrowLeft" /* ARROW_LEFT */, 1 /* MOVE_LEFT */], ["ArrowDown" /* ARROW_DOWN */, 2 /* MOVE_BACKWARD */], ["ArrowRight" /* ARROW_RIGHT */, 3 /* MOVE_RIGHT */], ["KeyX" /* KEY_X */, 4 /* MOVE_DOWN */], ["Space" /* SPACE */, 5 /* MOVE_UP */], ["PageDown" /* PAGE_DOWN */, 6 /* ZOOM_OUT */], ["PageUp" /* PAGE_UP */, 7 /* ZOOM_IN */], ["ShiftLeft" /* SHIFT_LEFT */, 8 /* BOOST */] ])); this.pointerBindings = new Bindings(); this.pointerBindings.setDefault(/* @__PURE__ */ new Map([ [0 /* MAIN */, 9 /* ROTATE */] ])); this.general = new GeneralSettings(); this.pointer = new PointerSettings(); this.rotation = new RotationSettings(); this.translation = new TranslationSettings(); this.zoom = new ZoomSettings(); this.general.addEventListener(_Settings.EVENT_CHANGE, (e) => this.dispatchEvent(e)); this.pointer.addEventListener(_Settings.EVENT_CHANGE, (e) => this.dispatchEvent(e)); this.rotation.addEventListener(_Settings.EVENT_CHANGE, (e) => this.dispatchEvent(e)); this.translation.addEventListener(_Settings.EVENT_CHANGE, (e) => this.dispatchEvent(e)); this.zoom.addEventListener(_Settings.EVENT_CHANGE, (e) => this.dispatchEvent(e)); } /** * Copies the given settings. * * @param settings - Settings. * @return This instance. */ copy(settings) { this.keyBindings.copy(settings.keyBindings); this.pointerBindings.copy(settings.pointerBindings); this.general.copy(settings.general); this.pointer.copy(settings.pointer); this.rotation.copy(settings.rotation); this.translation.copy(settings.translation); this.zoom.copy(settings.zoom); this.dispatchEvent({ type: _Settings.EVENT_CHANGE }); return this; } /** * Clones these settings. * * @return The cloned settings. */ clone() { const clone = new _Settings(); return clone.copy(this); } /** * Copies the given JSON data. * * @param json - The JSON data string. * @return This instance. */ fromJSON(json) { const settings = JSON.parse(json); this.keyBindings.fromJSON(settings.keyBindings); this.pointerBindings.fromJSON(settings.pointerBindings); this.general.fromJSON(settings.general); this.pointer.fromJSON(settings.pointer); this.rotation.fromJSON(settings.rotation); this.translation.fromJSON(settings.translation); this.zoom.fromJSON(settings.zoom); this.dispatchEvent({ type: _Settings.EVENT_CHANGE }); return this; } /** * Exports these settings as a data blob. * * @return The settings blob. */ toBlob() { return new Blob([JSON.stringify(this)], { type: "text/json" }); } toJSON() { return { keyBindings: this.keyBindings, pointerBindings: this.pointerBindings, general: this.general, pointer: this.pointer, rotation: this.rotation, translation: this.translation, zoom: this.zoom }; } }; // src/core/RotationControls.ts var v2 = /* @__PURE__ */ new Vector35(); var p = /* @__PURE__ */ new Vector2(); var RotationControls = class _RotationControls extends EventDispatcher8 { /** * Triggers when the quaternion is changed. * * @event */ static EVENT_UPDATE = "update"; /** * @see {@link domElement} */ _domElement; /** * A rotation manager. */ rotationManager; /** * A map that links actions to specific strategies. */ strategies; /** * Indicates whether the user is currently holding the pointer button down. */ dragging; /** * @see {@link enabled} */ _enabled; /** * The control settings. */ settings; /** * Constructs new controls. * * @param position - A position. * @param quaternion - A quaternion. * @param target - A target. * @param settings - The settings. */ constructor(position = new Vector35(), quaternion = new Quaternion2(), target = new Vector35(), settings = new Settings()) { super(); this._domElement = null; this._enabled = false; this.dragging = false; this.settings = settings; settings.addEventListener("change", (e) => this.handleEvent(e)); this.rotationManager = new RotationManager(position, quaternion, target, settings); this.rotationManager.addEventListener(_RotationControls.EVENT_UPDATE, (e) => this.dispatchEvent(e)); this.strategies = /* @__PURE__ */ new Map([ [6 /* ZOOM_OUT */, new ZoomStrategy(this.rotationManager, false)], [7 /* ZOOM_IN */, new ZoomStrategy(this.rotationManager, true)], [9 /* ROTATE */, new RotationStrategy(this)] ]); } /** * A DOM element. Acts as the primary event target. */ get domElement() { return this._domElement; } set domElement(value) { this._domElement = value; const enabled = this.enabled; this.dispose(); this.enabled = enabled; } /** * The position. */ get position() { return this.rotationManager.position; } set position(value) { this.rotationManager.position = value; } /** * The quaternion. */ get quaternion() { return this.rotationManager.quaternion; } set quaternion(value) { this.rotationManager.quaternion = value; } /** * The target. */ get target() { return this.rotationManager.target; } set target(value) { this.rotationManager.target = value; } /** * Looks at the given point. * * @param x - The X-coordinate, or a point. * @param y - The Y-coordinate. * @param z - The Z-coordinate. * @return This instance. */ lookAt(x2, y2, z2) { if (x2 instanceof Vector35) { this.rotationManager.lookAt(x2); } else { this.rotationManager.lookAt(v2.set(x2, y2, z2)); } return this; } /** * Returns the current view direction. * * @param view - A vector to store the direction in. * @return The normalized view direction. */ getViewDirection(view) { return this.rotationManager.getViewDirection(view); } /** * Returns the projected view direction. * * The projected direction will be reached if there are no further rotation adjustments. If damping is disabled, * the vector will be equal to the direction returned by {@link getViewDirection}. * * @param view - A vector to store the direction in. * @return The normalized view direction. */ getProjectedViewDirection(view) { return this.rotationManager.getProjectedViewDirection(view); } /** * Indicates whether the controls