/*! * camera-controls * https://github.com/yomotsu/camera-controls * (c) 2017 @yomotsu * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.CameraControls = factory()); }(this, function () { 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } // based on https://github.com/mrdoob/eventdispatcher.js/ var EventDispatcher = /*#__PURE__*/ function () { function EventDispatcher() { _classCallCheck(this, EventDispatcher); this._listeners = {}; } _createClass(EventDispatcher, [{ key: "addEventListener", value: function addEventListener(type, listener) { var listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } }, { key: "hasEventListener", value: function hasEventListener(type, listener) { var listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } }, { key: "removeEventListener", value: function removeEventListener(type, listener) { var listeners = this._listeners; var listenerArray = listeners[type]; if (listenerArray !== undefined) { var index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } }, { key: "dispatchEvent", value: function dispatchEvent(event) { var listeners = this._listeners; var listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; var array = listenerArray.slice(0); for (var i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } } } }]); return EventDispatcher; }(); var THREE; var _AXIS_Y; var _v2; var _v3A; var _v3B; var _v3C; var _v4; var _xColumn; var _yColumn; var _sphericalA; var _sphericalB; var EPSILON = 0.001; var PI_2 = Math.PI * 2; var STATE = { NONE: -1, ROTATE: 0, DOLLY: 1, TRUCK: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_TRUCK: 4, TOUCH_TRUCK: 5 }; var CameraControls = /*#__PURE__*/ function (_EventDispatcher) { _inherits(CameraControls, _EventDispatcher); _createClass(CameraControls, null, [{ key: "install", value: function install(libs) { THREE = libs.THREE; _AXIS_Y = new THREE.Vector3(0, 1, 0); _v2 = new THREE.Vector2(); _v3A = new THREE.Vector3(); _v3B = new THREE.Vector3(); _v3C = new THREE.Vector3(); _v4 = new THREE.Vector4(); _xColumn = new THREE.Vector3(); _yColumn = new THREE.Vector3(); _sphericalA = new THREE.Spherical(); _sphericalB = new THREE.Spherical(); } }]); function CameraControls(camera, domElement) { var _this; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; _classCallCheck(this, CameraControls); _this = _possibleConstructorReturn(this, _getPrototypeOf(CameraControls).call(this)); _this._camera = camera; _this._yAxisUpSpace = new THREE.Quaternion().setFromUnitVectors(_this._camera.up, _AXIS_Y); _this._yAxisUpSpaceInverse = _this._yAxisUpSpace.clone().inverse(); _this._state = STATE.NONE; _this.enabled = true; if (_this._camera.isPerspectiveCamera) { // How far you can dolly in and out ( PerspectiveCamera only ) _this.minDistance = 0; _this.maxDistance = Infinity; } else if (_this._camera.isOrthographicCamera) { // How far you can zoom in and out ( OrthographicCamera only ) _this.minZoom = 0; _this.maxZoom = Infinity; } _this.minPolarAngle = 0; // radians _this.maxPolarAngle = Math.PI/2; // radians /2 keeps it from going below horizion. _this.minAzimuthAngle = -Infinity; // radians _this.maxAzimuthAngle = Infinity; // radians // Target cannot move outside of this box _this._boundary = new THREE.Box3(new THREE.Vector3(-Infinity, -Infinity, -Infinity), new THREE.Vector3(Infinity, Infinity, Infinity)); _this.boundaryFriction = 0.0; _this._boundaryEnclosesCamera = false; _this.dampingFactor = 0.05; _this.draggingDampingFactor = 0.25; _this.azimuthRotateSpeed = 1.0; _this.polarRotateSpeed = 1.0; _this.dollySpeed = 1.0; _this.truckSpeed = 2.0; _this.dollyToCursor = false; _this.verticalDragToForward = false; _this._domElement = domElement; _this._viewport = null; // the location of focus, where the object orbits around _this._target = new THREE.Vector3(); _this._targetEnd = new THREE.Vector3(); // rotation _this._spherical = new THREE.Spherical().setFromVector3(_this._camera.position.clone().applyQuaternion(_this._yAxisUpSpace)); _this._sphericalEnd = _this._spherical.clone(); // reset _this._target0 = _this._target.clone(); _this._position0 = _this._camera.position.clone(); _this._zoom0 = _this._camera.zoom; _this._dollyControlAmount = 0; _this._dollyControlCoord = new THREE.Vector2(); _this._hasUpdated = true; _this.update(0); if (!_this._domElement || options.ignoreDOMEventListeners) { _this._removeAllEventListeners = function () {}; } else { var extractClientCoordFromEvent = function extractClientCoordFromEvent(event, out) { out.set(0, 0); if (isTouchEvent(event)) { for (var i = 0; i < event.touches.length; i++) { out.x += event.touches[i].clientX; out.y += event.touches[i].clientY; } out.x /= event.touches.length; out.y /= event.touches.length; return out; } else { out.set(event.clientX, event.clientY); return out; } }; var onMouseDown = function onMouseDown(event) { if (!scope.enabled) return; event.preventDefault(); var prevState = scope._state; //changed to match CURA switch (event.button) { case THREE.MOUSE.LEFT: scope._state = STATE.TRUCK; break; case THREE.MOUSE.MIDDLE: scope._state = STATE.TRUCK; //scope._state = STATE.DOLLY; break; case THREE.MOUSE.RIGHT: scope._state = STATE.ROTATE; break; } if (prevState !== scope._state) { startDragging(event); } }; var onTouchStart = function onTouchStart(event) { if (!scope.enabled) return; event.preventDefault(); var prevState = scope._state; switch (event.touches.length) { case 1: // one-fingered touch: rotate scope._state = STATE.TOUCH_ROTATE; break; case 2: // two-fingered touch: dolly scope._state = STATE.TOUCH_DOLLY_TRUCK; break; case 3: // three-fingered touch: truck scope._state = STATE.TOUCH_TRUCK; break; } if (prevState !== scope._state) { startDragging(event); } }; var onMouseWheel = function onMouseWheel(event) { if (!scope.enabled) return; event.preventDefault(); // Ref: https://github.com/cedricpinson/osgjs/blob/00e5a7e9d9206c06fdde0436e1d62ab7cb5ce853/sources/osgViewer/input/source/InputSourceMouse.js#L89-L103 var mouseDeltaFactor = 120; var deltaYFactor = navigator.platform.indexOf('Mac') === 0 ? -1 : -3; var delta; if (event.wheelDelta !== undefined) { delta = event.wheelDelta / mouseDeltaFactor; } else if (event.deltaMode === 1) { delta = event.deltaY / deltaYFactor; } else { delta = event.deltaY / (10 * deltaYFactor); } var x, y; if (scope.dollyToCursor) { elementRect = scope._getClientRect(_v4); x = (event.clientX - elementRect.x) / elementRect.z * 2 - 1; y = (event.clientY - elementRect.y) / elementRect.w * -2 + 1; } dollyInternal(-delta, x, y); }; var onContextMenu = function onContextMenu(event) { if (!scope.enabled) return; event.preventDefault(); }; var startDragging = function startDragging(event) { if (!scope.enabled) return; event.preventDefault(); extractClientCoordFromEvent(event, _v2); elementRect = scope._getClientRect(_v4); dragStart.copy(_v2); if (scope._state === STATE.TOUCH_DOLLY_TRUCK) { // 2 finger pinch var dx = _v2.x - event.touches[1].clientX; var dy = _v2.y - event.touches[1].clientY; var distance = Math.sqrt(dx * dx + dy * dy); dollyStart.set(0, distance); // center coords of 2 finger truck var x = (event.touches[0].clientX + event.touches[1].clientX) * 0.5; var y = (event.touches[0].clientY + event.touches[1].clientY) * 0.5; dragStart.set(x, y); } document.addEventListener('mousemove', dragging, { passive: false }); document.addEventListener('touchmove', dragging, { passive: false }); document.addEventListener('mouseup', endDragging); document.addEventListener('touchend', endDragging); scope.dispatchEvent({ type: 'controlstart', originalEvent: event }); }; var dragging = function dragging(event) { if (!scope.enabled) return; event.preventDefault(); extractClientCoordFromEvent(event, _v2); var deltaX = dragStart.x - _v2.x; var deltaY = dragStart.y - _v2.y; dragStart.copy(_v2); switch (scope._state) { case STATE.ROTATE: case STATE.TOUCH_ROTATE: var theta = PI_2 * scope.azimuthRotateSpeed * deltaX / elementRect.z; var phi = PI_2 * scope.polarRotateSpeed * deltaY / elementRect.w; scope.rotate(theta, phi, true); break; case STATE.DOLLY: // not implemented break; case STATE.TOUCH_DOLLY_TRUCK: var dx = _v2.x - event.touches[1].clientX; var dy = _v2.y - event.touches[1].clientY; var distance = Math.sqrt(dx * dx + dy * dy); var dollyDelta = dollyStart.y - distance; var touchDollyFactor = 8; var dollyX = scope.dollyToCursor ? (dragStart.x - elementRect.x) / elementRect.z * 2 - 1 : 0; var dollyY = scope.dollyToCursor ? (dragStart.y - elementRect.y) / elementRect.w * -2 + 1 : 0; dollyInternal(dollyDelta / touchDollyFactor, dollyX, dollyY); dollyStart.set(0, distance); truckInternal(deltaX, deltaY); break; case STATE.TRUCK: case STATE.TOUCH_TRUCK: truckInternal(deltaX, deltaY); break; } scope.dispatchEvent({ type: 'control', originalEvent: event }); }; var endDragging = function endDragging(event) { if (!scope.enabled) return; scope._state = STATE.NONE; document.removeEventListener('mousemove', dragging, { passive: false }); document.removeEventListener('touchmove', dragging, { passive: false }); document.removeEventListener('mouseup', endDragging); document.removeEventListener('touchend', endDragging); scope.dispatchEvent({ type: 'controlend', originalEvent: event }); }; var truckInternal = function truckInternal(deltaX, deltaY) { if (scope._camera.isPerspectiveCamera) { var offset = _v3A.copy(scope._camera.position).sub(scope._target); // half of the fov is center to top of screen var fovInRad = scope._camera.fov * THREE.Math.DEG2RAD; var targetDistance = offset.length() * Math.tan(fovInRad / 2); var truckX = scope.truckSpeed * deltaX * targetDistance / elementRect.w; var pedestalY = scope.truckSpeed * deltaY * targetDistance / elementRect.w; if (scope.verticalDragToForward) { scope.truck(truckX, 0, true); scope.forward(-pedestalY, true); } else { scope.truck(truckX, pedestalY, true); } } else if (scope._camera.isOrthographicCamera) { // orthographic var _truckX = deltaX * (scope._camera.right - scope._camera.left) / scope._camera.zoom / elementRect.z; var _pedestalY = deltaY * (scope._camera.top - scope._camera.bottom) / scope._camera.zoom / elementRect.w; scope.truck(_truckX, _pedestalY, true); } }; var dollyInternal = function dollyInternal(delta, x, y) { var dollyScale = Math.pow(0.95, -delta * scope.dollySpeed); if (scope._camera.isPerspectiveCamera) { var distance = scope._sphericalEnd.radius * dollyScale - scope._sphericalEnd.radius; var prevRadius = scope._sphericalEnd.radius; scope.dolly(distance); if (scope.dollyToCursor) { scope._dollyControlAmount += scope._sphericalEnd.radius - prevRadius; scope._dollyControlCoord.set(x, y); } } else if (scope._camera.isOrthographicCamera) { scope._camera.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope._camera.zoom * dollyScale)); scope._camera.updateProjectionMatrix(); scope._hasUpdated = true; } }; var scope = _assertThisInitialized(_this); var dragStart = new THREE.Vector2(); var dollyStart = new THREE.Vector2(); var elementRect; _this._domElement.addEventListener('mousedown', onMouseDown); _this._domElement.addEventListener('touchstart', onTouchStart); _this._domElement.addEventListener('wheel', onMouseWheel); _this._domElement.addEventListener('contextmenu', onContextMenu); _this._removeAllEventListeners = function () { scope._domElement.removeEventListener('mousedown', onMouseDown); scope._domElement.removeEventListener('touchstart', onTouchStart); scope._domElement.removeEventListener('wheel', onMouseWheel); scope._domElement.removeEventListener('contextmenu', onContextMenu); document.removeEventListener('mousemove', dragging); document.removeEventListener('touchmove', dragging); document.removeEventListener('mouseup', endDragging); document.removeEventListener('touchend', endDragging); }; } return _this; } // wrong. phi should be map to polar, but backward compatibility. _createClass(CameraControls, [{ key: "rotate", // azimuthAngle in radian // polarAngle in radian value: function rotate(azimuthAngle, polarAngle, enableTransition) { this.rotateTo(this._sphericalEnd.theta + azimuthAngle, this._sphericalEnd.phi + polarAngle, enableTransition); } // azimuthAngle in radian // polarAngle in radian }, { key: "rotateTo", value: function rotateTo(azimuthAngle, polarAngle, enableTransition) { var theta = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, azimuthAngle)); var phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, polarAngle)); this._sphericalEnd.theta = theta; this._sphericalEnd.phi = phi; this._sphericalEnd.makeSafe(); if (!enableTransition) { this._spherical.theta = this._sphericalEnd.theta; this._spherical.phi = this._sphericalEnd.phi; } this._hasUpdated = true; } }, { key: "dolly", value: function dolly(distance, enableTransition) { if (this._camera.isOrthographicCamera) { console.warn('dolly is not available for OrthographicCamera'); return; } this.dollyTo(this._sphericalEnd.radius + distance, enableTransition); } }, { key: "dollyTo", value: function dollyTo(distance, enableTransition) { if (this._camera.isOrthographicCamera) { console.warn('dolly is not available for OrthographicCamera'); return; } this._sphericalEnd.radius = THREE.Math.clamp(distance, this.minDistance, this.maxDistance); if (!enableTransition) { this._spherical.radius = this._sphericalEnd.radius; } this._hasUpdated = true; } }, { key: "pan", value: function pan(x, y, enableTransition) { console.log('`pan` has been renamed to `truck`'); this.truck(x, y, enableTransition); } }, { key: "truck", value: function truck(x, y, enableTransition) { this._camera.updateMatrix(); _xColumn.setFromMatrixColumn(this._camera.matrix, 0); _yColumn.setFromMatrixColumn(this._camera.matrix, 1); _xColumn.multiplyScalar(x); _yColumn.multiplyScalar(-y); var offset = _v3A.copy(_xColumn).add(_yColumn); this._encloseToBoundary(this._targetEnd, offset, this.boundaryFriction); if (!enableTransition) { this._target.copy(this._targetEnd); } this._hasUpdated = true; } }, { key: "forward", value: function forward(distance, enableTransition) { _v3A.setFromMatrixColumn(this._camera.matrix, 0); _v3A.crossVectors(this._camera.up, _v3A); _v3A.multiplyScalar(distance); this._encloseToBoundary(this._targetEnd, _v3A, this.boundaryFriction); if (!enableTransition) { this._target.copy(this._targetEnd); } this._hasUpdated = true; } }, { key: "moveTo", value: function moveTo(x, y, z, enableTransition) { this._targetEnd.set(x, y, z); if (!enableTransition) { this._target.copy(this._targetEnd); } this._hasUpdated = true; } }, { key: "fitTo", value: function fitTo(box3OrObject, enableTransition) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (this._camera.isOrthographicCamera) { console.warn('fitTo is not supported for OrthographicCamera'); return; } var paddingLeft = options.paddingLeft || 0; var paddingRight = options.paddingRight || 0; var paddingBottom = options.paddingBottom || 0; var paddingTop = options.paddingTop || 0; var boundingBox = box3OrObject.isBox3 ? box3OrObject.clone() : new THREE.Box3().setFromObject(box3OrObject); var size = boundingBox.getSize(_v3A); var boundingWidth = size.x + paddingLeft + paddingRight; var boundingHeight = size.y + paddingTop + paddingBottom; var boundingDepth = size.z; var distance = this.getDistanceToFit(boundingWidth, boundingHeight, boundingDepth); this.dollyTo(distance, enableTransition); var boundingBoxCenter = boundingBox.getCenter(_v3A); var cx = boundingBoxCenter.x - (paddingLeft * 0.5 - paddingRight * 0.5); var cy = boundingBoxCenter.y + (paddingTop * 0.5 - paddingBottom * 0.5); var cz = boundingBoxCenter.z; this.moveTo(cx, cy, cz, enableTransition); this._sanitizeSphericals(); this.rotateTo(0, 90 * THREE.Math.DEG2RAD, enableTransition); } }, { key: "setLookAt", value: function setLookAt(positionX, positionY, positionZ, targetX, targetY, targetZ, enableTransition) { var position = _v3A.set(positionX, positionY, positionZ); var target = _v3B.set(targetX, targetY, targetZ); this._targetEnd.copy(target); this._sphericalEnd.setFromVector3(position.sub(target).applyQuaternion(this._yAxisUpSpace)); this._sanitizeSphericals(); if (!enableTransition) { this._target.copy(this._targetEnd); this._spherical.copy(this._sphericalEnd); } this._hasUpdated = true; } }, { key: "lerpLookAt", value: function lerpLookAt(positionAX, positionAY, positionAZ, targetAX, targetAY, targetAZ, positionBX, positionBY, positionBZ, targetBX, targetBY, targetBZ, t, enableTransition) { var positionA = _v3A.set(positionAX, positionAY, positionAZ); var targetA = _v3B.set(targetAX, targetAY, targetAZ); _sphericalA.setFromVector3(positionA.sub(targetA).applyQuaternion(this._yAxisUpSpace)); var targetB = _v3A.set(targetBX, targetBY, targetBZ); this._targetEnd.copy(targetA).lerp(targetB, t); // tricky var positionB = _v3B.set(positionBX, positionBY, positionBZ); _sphericalB.setFromVector3(positionB.sub(targetB).applyQuaternion(this._yAxisUpSpace)); var deltaTheta = _sphericalB.theta - _sphericalA.theta; var deltaPhi = _sphericalB.phi - _sphericalA.phi; var deltaRadius = _sphericalB.radius - _sphericalA.radius; this._sphericalEnd.set(_sphericalA.radius + deltaRadius * t, _sphericalA.phi + deltaPhi * t, _sphericalA.theta + deltaTheta * t); this._sanitizeSphericals(); if (!enableTransition) { this._target.copy(this._targetEnd); this._spherical.copy(this._sphericalEnd); } this._hasUpdated = true; } }, { key: "setPosition", value: function setPosition(positionX, positionY, positionZ, enableTransition) { this.setLookAt(positionX, positionY, positionZ, this._targetEnd.x, this._targetEnd.y, this._targetEnd.z, enableTransition); } }, { key: "setTarget", value: function setTarget(targetX, targetY, targetZ, enableTransition) { var pos = this.getPosition(_v3A); this.setLookAt(pos.x, pos.y, pos.z, targetX, targetY, targetZ, enableTransition); } }, { key: "setBoundary", value: function setBoundary(box3) { if (!box3) { this._boundary.min.set(-Infinity, -Infinity, -Infinity); this._boundary.max.set(Infinity, Infinity, Infinity); this._hasUpdated = true; return; } this._boundary.copy(box3); this._boundary.clampPoint(this._targetEnd, this._targetEnd); this._hasUpdated = true; } }, { key: "setViewport", value: function setViewport(viewportOrX, y, width, height) { if (viewportOrX === null) { // null this._viewport = null; return; } this._viewport = this._viewport || new THREE.Vector4(); if (typeof viewportOrX === 'number') { // number this._viewport.set(viewportOrX, y, width, height); } else { // Vector4 this._viewport.copy(viewportOrX); } } }, { key: "getDistanceToFit", value: function getDistanceToFit(width, height, depth) { var camera = this._camera; var boundingRectAspect = width / height; var fov = camera.fov * THREE.Math.DEG2RAD; var aspect = camera.aspect; var heightToFit = boundingRectAspect < aspect ? height : width / aspect; return heightToFit * 0.5 / Math.tan(fov * 0.5) + depth * 0.5; } }, { key: "getTarget", value: function getTarget(out) { var _out = !!out && out.isVector3 ? out : new THREE.Vector3(); return _out.copy(this._targetEnd); } }, { key: "getPosition", value: function getPosition(out) { var _out = !!out && out.isVector3 ? out : new THREE.Vector3(); return _out.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse).add(this._targetEnd); } }, { key: "reset", value: function reset(enableTransition) { this.setLookAt(this._position0.x, this._position0.y, this._position0.z, this._target0.x, this._target0.y, this._target0.z, enableTransition); } }, { key: "saveState", value: function saveState() { this._target0.copy(this._target); this._position0.copy(this._camera.position); this._zoom0 = this._camera.zoom; } }, { key: "updateCameraUp", value: function updateCameraUp() { this._yAxisUpSpace.setFromUnitVectors(this._camera.up, _AXIS_Y); this._yAxisUpSpaceInverse.copy(this._yAxisUpSpace).inverse(); } }, { key: "update", value: function update(delta) { var currentDampingFactor = this._state === STATE.NONE ? this.dampingFactor : this.draggingDampingFactor; var lerpRatio = 1.0 - Math.exp(-currentDampingFactor * delta / 0.016); var deltaTheta = this._sphericalEnd.theta - this._spherical.theta; var deltaPhi = this._sphericalEnd.phi - this._spherical.phi; var deltaRadius = this._sphericalEnd.radius - this._spherical.radius; var deltaTarget = _v3A.subVectors(this._targetEnd, this._target); if (Math.abs(deltaTheta) > EPSILON || Math.abs(deltaPhi) > EPSILON || Math.abs(deltaRadius) > EPSILON || Math.abs(deltaTarget.x) > EPSILON || Math.abs(deltaTarget.y) > EPSILON || Math.abs(deltaTarget.z) > EPSILON) { this._spherical.set(this._spherical.radius + deltaRadius * lerpRatio, this._spherical.phi + deltaPhi * lerpRatio, this._spherical.theta + deltaTheta * lerpRatio); this._target.add(deltaTarget.multiplyScalar(lerpRatio)); this._hasUpdated = true; } else { this._spherical.copy(this._sphericalEnd); this._target.copy(this._targetEnd); } if (this._dollyControlAmount !== 0) { if (this._camera.isPerspectiveCamera) { var direction = _v3A.copy(_v3A.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse)).normalize().negate(); var planeX = _v3B.copy(direction).cross(_v3C.copy(this._camera.up)).normalize(); if (planeX.lengthSq() === 0) planeX.x = 1.0; var planeY = _v3C.crossVectors(planeX, direction); var worldToScreen = this._sphericalEnd.radius * Math.tan(this._camera.fov * THREE.Math.DEG2RAD * 0.5); var prevRadius = this._sphericalEnd.radius - this._dollyControlAmount; var _lerpRatio = (prevRadius - this._sphericalEnd.radius) / this._sphericalEnd.radius; var cursor = _v3A.copy(this._targetEnd).add(planeX.multiplyScalar(this._dollyControlCoord.x * worldToScreen * this._camera.aspect)).add(planeY.multiplyScalar(this._dollyControlCoord.y * worldToScreen)); this._targetEnd.lerp(cursor, _lerpRatio); this._target.copy(this._targetEnd); } this._dollyControlAmount = 0; } this._spherical.makeSafe(); this._camera.position.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse).add(this._target); this._camera.lookAt(this._target); if (this._boundaryEnclosesCamera) { this._encloseToBoundary(this._camera.position.copy(this._target), _v3A.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse), 1.0); } var updated = this._hasUpdated; this._hasUpdated = false; if (updated) this.dispatchEvent({ type: 'update' }); return updated; } }, { key: "toJSON", value: function toJSON() { return JSON.stringify({ enabled: this.enabled, minDistance: this.minDistance, maxDistance: infinityToMaxNumber(this.maxDistance), minPolarAngle: this.minPolarAngle, maxPolarAngle: infinityToMaxNumber(this.maxPolarAngle), minAzimuthAngle: infinityToMaxNumber(this.minAzimuthAngle), maxAzimuthAngle: infinityToMaxNumber(this.maxAzimuthAngle), dampingFactor: this.dampingFactor, draggingDampingFactor: this.draggingDampingFactor, dollySpeed: this.dollySpeed, truckSpeed: this.truckSpeed, dollyToCursor: this.dollyToCursor, verticalDragToForward: this.verticalDragToForward, target: this._targetEnd.toArray(), position: this._camera.position.toArray(), target0: this._target0.toArray(), position0: this._position0.toArray() }); } }, { key: "fromJSON", value: function fromJSON(json, enableTransition) { var obj = JSON.parse(json); var position = new THREE.Vector3().fromArray(obj.position); this.enabled = obj.enabled; this.minDistance = obj.minDistance; this.maxDistance = maxNumberToInfinity(obj.maxDistance); this.minPolarAngle = obj.minPolarAngle; this.maxPolarAngle = maxNumberToInfinity(obj.maxPolarAngle); this.minAzimuthAngle = maxNumberToInfinity(obj.minAzimuthAngle); this.maxAzimuthAngle = maxNumberToInfinity(obj.maxAzimuthAngle); this.dampingFactor = obj.dampingFactor; this.draggingDampingFactor = obj.draggingDampingFactor; this.dollySpeed = obj.dollySpeed; this.truckSpeed = obj.truckSpeed; this.dollyToCursor = obj.dollyToCursor; this.verticalDragToForward = obj.verticalDragToForward; this._target0.fromArray(obj.target0); this._position0.fromArray(obj.position0); this._targetEnd.fromArray(obj.target); this._sphericalEnd.setFromVector3(position.sub(this._target0).applyQuaternion(this._yAxisUpSpace)); if (!enableTransition) { this._target.copy(this._targetEnd); this._spherical.copy(this._sphericalEnd); } this._hasUpdated = true; } }, { key: "dispose", value: function dispose() { this._removeAllEventListeners(); } }, { key: "_encloseToBoundary", value: function _encloseToBoundary(position, offset, friction) { var offsetLength2 = offset.lengthSq(); if (offsetLength2 === 0.0) { // sanity check return position; } // See: https://twitter.com/FMS_Cat/status/1106508958640988161 var newTarget = _v3B.copy(offset).add(position); // target var clampedTarget = this._boundary.clampPoint(newTarget, _v3C); // clamped target var deltaClampedTarget = clampedTarget.sub(newTarget); // newTarget -> clampedTarget var deltaClampedTargetLength2 = deltaClampedTarget.lengthSq(); // squared length of deltaClampedTarget if (deltaClampedTargetLength2 === 0.0) { // when the position doesn't have to be clamped return position.add(offset); } else if (deltaClampedTargetLength2 === offsetLength2) { // when the position is completely stuck return position; } else if (friction === 0.0) { return position.add(offset).add(deltaClampedTarget); } else { var offsetFactor = 1.0 + friction * deltaClampedTargetLength2 / offset.dot(deltaClampedTarget); return position.add(_v3B.copy(offset).multiplyScalar(offsetFactor)).add(deltaClampedTarget.multiplyScalar(1.0 - friction)); } } }, { key: "_sanitizeSphericals", value: function _sanitizeSphericals() { this._sphericalEnd.theta = this._sphericalEnd.theta % PI_2; this._spherical.theta += PI_2 * Math.round((this._sphericalEnd.theta - this._spherical.theta) / PI_2); } /** * Get its client rect and package into given `THREE.Vector4` . */ }, { key: "_getClientRect", value: function _getClientRect(target) { var rect = this._domElement.getBoundingClientRect(); target.x = rect.left; target.y = rect.top; if (this._viewport) { target.x += this._viewport.x; target.y += rect.height - this._viewport.w - this._viewport.y; target.z = this._viewport.z; target.w = this._viewport.w; } else { target.z = rect.width; target.w = rect.height; } return target; } }, { key: "phiSpeed", set: function set(speed) { console.warn('phiSpeed was renamed. use azimuthRotateSpeed instead'); this.azimuthRotateSpeed = speed; } // wrong. theta should be map to azimuth, but backward compatibility. }, { key: "thetaSpeed", set: function set(speed) { console.warn('thetaSpeed was renamed. use polarRotateSpeed instead'); this.polarRotateSpeed = speed; } }, { key: "boundaryEnclosesCamera", get: function get() { return this._boundaryEnclosesCamera; }, set: function set(boundaryEnclosesCamera) { this._boundaryEnclosesCamera = boundaryEnclosesCamera; this._hasUpdated = true; } }]); return CameraControls; }(EventDispatcher); function isTouchEvent(event) { return 'TouchEvent' in window && event instanceof TouchEvent; } function infinityToMaxNumber(value) { if (isFinite(value)) return value; if (value < 0) return -Number.MAX_VALUE; return Number.MAX_VALUE; } function maxNumberToInfinity(value) { if (Math.abs(value) < Number.MAX_VALUE) return value; return value * Infinity; } return CameraControls; }));