bambuddy/gcode_viewer/js/camera-controls.js
Nathen Fredrick 3adce435ee
feat: add embedded GCode viewer (#963)
* feat: add embedded GCode viewer

Adds PrettyGCode as a built-in GCode visualiser embedded directly in the
Bambuddy layout, so users can preview and inspect GCode files without
leaving the dashboard.
2026-04-22 11:14:42 +02:00

1070 lines
35 KiB
JavaScript

/*!
* 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;
}));