mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
* 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.
1070 lines
35 KiB
JavaScript
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;
|
|
|
|
}));
|