camera-controls.js 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. /*!
  2. * camera-controls
  3. * https://github.com/yomotsu/camera-controls
  4. * (c) 2017 @yomotsu
  5. * Released under the MIT License.
  6. */
  7. (function (global, factory) {
  8. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  9. typeof define === 'function' && define.amd ? define(factory) :
  10. (global = global || self, global.CameraControls = factory());
  11. }(this, function () { 'use strict';
  12. function _classCallCheck(instance, Constructor) {
  13. if (!(instance instanceof Constructor)) {
  14. throw new TypeError("Cannot call a class as a function");
  15. }
  16. }
  17. function _defineProperties(target, props) {
  18. for (var i = 0; i < props.length; i++) {
  19. var descriptor = props[i];
  20. descriptor.enumerable = descriptor.enumerable || false;
  21. descriptor.configurable = true;
  22. if ("value" in descriptor) descriptor.writable = true;
  23. Object.defineProperty(target, descriptor.key, descriptor);
  24. }
  25. }
  26. function _createClass(Constructor, protoProps, staticProps) {
  27. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  28. if (staticProps) _defineProperties(Constructor, staticProps);
  29. return Constructor;
  30. }
  31. function _inherits(subClass, superClass) {
  32. if (typeof superClass !== "function" && superClass !== null) {
  33. throw new TypeError("Super expression must either be null or a function");
  34. }
  35. subClass.prototype = Object.create(superClass && superClass.prototype, {
  36. constructor: {
  37. value: subClass,
  38. writable: true,
  39. configurable: true
  40. }
  41. });
  42. if (superClass) _setPrototypeOf(subClass, superClass);
  43. }
  44. function _getPrototypeOf(o) {
  45. _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
  46. return o.__proto__ || Object.getPrototypeOf(o);
  47. };
  48. return _getPrototypeOf(o);
  49. }
  50. function _setPrototypeOf(o, p) {
  51. _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
  52. o.__proto__ = p;
  53. return o;
  54. };
  55. return _setPrototypeOf(o, p);
  56. }
  57. function _assertThisInitialized(self) {
  58. if (self === void 0) {
  59. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  60. }
  61. return self;
  62. }
  63. function _possibleConstructorReturn(self, call) {
  64. if (call && (typeof call === "object" || typeof call === "function")) {
  65. return call;
  66. }
  67. return _assertThisInitialized(self);
  68. }
  69. // based on https://github.com/mrdoob/eventdispatcher.js/
  70. var EventDispatcher =
  71. /*#__PURE__*/
  72. function () {
  73. function EventDispatcher() {
  74. _classCallCheck(this, EventDispatcher);
  75. this._listeners = {};
  76. }
  77. _createClass(EventDispatcher, [{
  78. key: "addEventListener",
  79. value: function addEventListener(type, listener) {
  80. var listeners = this._listeners;
  81. if (listeners[type] === undefined) {
  82. listeners[type] = [];
  83. }
  84. if (listeners[type].indexOf(listener) === -1) {
  85. listeners[type].push(listener);
  86. }
  87. }
  88. }, {
  89. key: "hasEventListener",
  90. value: function hasEventListener(type, listener) {
  91. var listeners = this._listeners;
  92. return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1;
  93. }
  94. }, {
  95. key: "removeEventListener",
  96. value: function removeEventListener(type, listener) {
  97. var listeners = this._listeners;
  98. var listenerArray = listeners[type];
  99. if (listenerArray !== undefined) {
  100. var index = listenerArray.indexOf(listener);
  101. if (index !== -1) {
  102. listenerArray.splice(index, 1);
  103. }
  104. }
  105. }
  106. }, {
  107. key: "dispatchEvent",
  108. value: function dispatchEvent(event) {
  109. var listeners = this._listeners;
  110. var listenerArray = listeners[event.type];
  111. if (listenerArray !== undefined) {
  112. event.target = this;
  113. var array = listenerArray.slice(0);
  114. for (var i = 0, l = array.length; i < l; i++) {
  115. array[i].call(this, event);
  116. }
  117. }
  118. }
  119. }]);
  120. return EventDispatcher;
  121. }();
  122. var THREE;
  123. var _AXIS_Y;
  124. var _v2;
  125. var _v3A;
  126. var _v3B;
  127. var _v3C;
  128. var _v4;
  129. var _xColumn;
  130. var _yColumn;
  131. var _sphericalA;
  132. var _sphericalB;
  133. var EPSILON = 0.001;
  134. var PI_2 = Math.PI * 2;
  135. var STATE = {
  136. NONE: -1,
  137. ROTATE: 0,
  138. DOLLY: 1,
  139. TRUCK: 2,
  140. TOUCH_ROTATE: 3,
  141. TOUCH_DOLLY_TRUCK: 4,
  142. TOUCH_TRUCK: 5
  143. };
  144. var CameraControls =
  145. /*#__PURE__*/
  146. function (_EventDispatcher) {
  147. _inherits(CameraControls, _EventDispatcher);
  148. _createClass(CameraControls, null, [{
  149. key: "install",
  150. value: function install(libs) {
  151. THREE = libs.THREE;
  152. _AXIS_Y = new THREE.Vector3(0, 1, 0);
  153. _v2 = new THREE.Vector2();
  154. _v3A = new THREE.Vector3();
  155. _v3B = new THREE.Vector3();
  156. _v3C = new THREE.Vector3();
  157. _v4 = new THREE.Vector4();
  158. _xColumn = new THREE.Vector3();
  159. _yColumn = new THREE.Vector3();
  160. _sphericalA = new THREE.Spherical();
  161. _sphericalB = new THREE.Spherical();
  162. }
  163. }]);
  164. function CameraControls(camera, domElement) {
  165. var _this;
  166. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  167. _classCallCheck(this, CameraControls);
  168. _this = _possibleConstructorReturn(this, _getPrototypeOf(CameraControls).call(this));
  169. _this._camera = camera;
  170. _this._yAxisUpSpace = new THREE.Quaternion().setFromUnitVectors(_this._camera.up, _AXIS_Y);
  171. _this._yAxisUpSpaceInverse = _this._yAxisUpSpace.clone().inverse();
  172. _this._state = STATE.NONE;
  173. _this.enabled = true;
  174. if (_this._camera.isPerspectiveCamera) {
  175. // How far you can dolly in and out ( PerspectiveCamera only )
  176. _this.minDistance = 0;
  177. _this.maxDistance = Infinity;
  178. } else if (_this._camera.isOrthographicCamera) {
  179. // How far you can zoom in and out ( OrthographicCamera only )
  180. _this.minZoom = 0;
  181. _this.maxZoom = Infinity;
  182. }
  183. _this.minPolarAngle = 0; // radians
  184. _this.maxPolarAngle = Math.PI/2; // radians /2 keeps it from going below horizion.
  185. _this.minAzimuthAngle = -Infinity; // radians
  186. _this.maxAzimuthAngle = Infinity; // radians
  187. // Target cannot move outside of this box
  188. _this._boundary = new THREE.Box3(new THREE.Vector3(-Infinity, -Infinity, -Infinity), new THREE.Vector3(Infinity, Infinity, Infinity));
  189. _this.boundaryFriction = 0.0;
  190. _this._boundaryEnclosesCamera = false;
  191. _this.dampingFactor = 0.05;
  192. _this.draggingDampingFactor = 0.25;
  193. _this.azimuthRotateSpeed = 1.0;
  194. _this.polarRotateSpeed = 1.0;
  195. _this.dollySpeed = 1.0;
  196. _this.truckSpeed = 2.0;
  197. _this.dollyToCursor = false;
  198. _this.verticalDragToForward = false;
  199. _this._domElement = domElement;
  200. _this._viewport = null; // the location of focus, where the object orbits around
  201. _this._target = new THREE.Vector3();
  202. _this._targetEnd = new THREE.Vector3(); // rotation
  203. _this._spherical = new THREE.Spherical().setFromVector3(_this._camera.position.clone().applyQuaternion(_this._yAxisUpSpace));
  204. _this._sphericalEnd = _this._spherical.clone(); // reset
  205. _this._target0 = _this._target.clone();
  206. _this._position0 = _this._camera.position.clone();
  207. _this._zoom0 = _this._camera.zoom;
  208. _this._dollyControlAmount = 0;
  209. _this._dollyControlCoord = new THREE.Vector2();
  210. _this._hasUpdated = true;
  211. _this.update(0);
  212. if (!_this._domElement || options.ignoreDOMEventListeners) {
  213. _this._removeAllEventListeners = function () {};
  214. } else {
  215. var extractClientCoordFromEvent = function extractClientCoordFromEvent(event, out) {
  216. out.set(0, 0);
  217. if (isTouchEvent(event)) {
  218. for (var i = 0; i < event.touches.length; i++) {
  219. out.x += event.touches[i].clientX;
  220. out.y += event.touches[i].clientY;
  221. }
  222. out.x /= event.touches.length;
  223. out.y /= event.touches.length;
  224. return out;
  225. } else {
  226. out.set(event.clientX, event.clientY);
  227. return out;
  228. }
  229. };
  230. var onMouseDown = function onMouseDown(event) {
  231. if (!scope.enabled) return;
  232. event.preventDefault();
  233. var prevState = scope._state;
  234. //changed to match CURA
  235. switch (event.button) {
  236. case THREE.MOUSE.LEFT:
  237. scope._state = STATE.TRUCK;
  238. break;
  239. case THREE.MOUSE.MIDDLE:
  240. scope._state = STATE.TRUCK;
  241. //scope._state = STATE.DOLLY;
  242. break;
  243. case THREE.MOUSE.RIGHT:
  244. scope._state = STATE.ROTATE;
  245. break;
  246. }
  247. if (prevState !== scope._state) {
  248. startDragging(event);
  249. }
  250. };
  251. var onTouchStart = function onTouchStart(event) {
  252. if (!scope.enabled) return;
  253. event.preventDefault();
  254. var prevState = scope._state;
  255. switch (event.touches.length) {
  256. case 1:
  257. // one-fingered touch: rotate
  258. scope._state = STATE.TOUCH_ROTATE;
  259. break;
  260. case 2:
  261. // two-fingered touch: dolly
  262. scope._state = STATE.TOUCH_DOLLY_TRUCK;
  263. break;
  264. case 3:
  265. // three-fingered touch: truck
  266. scope._state = STATE.TOUCH_TRUCK;
  267. break;
  268. }
  269. if (prevState !== scope._state) {
  270. startDragging(event);
  271. }
  272. };
  273. var onMouseWheel = function onMouseWheel(event) {
  274. if (!scope.enabled) return;
  275. event.preventDefault(); // Ref: https://github.com/cedricpinson/osgjs/blob/00e5a7e9d9206c06fdde0436e1d62ab7cb5ce853/sources/osgViewer/input/source/InputSourceMouse.js#L89-L103
  276. var mouseDeltaFactor = 120;
  277. var deltaYFactor = navigator.platform.indexOf('Mac') === 0 ? -1 : -3;
  278. var delta;
  279. if (event.wheelDelta !== undefined) {
  280. delta = event.wheelDelta / mouseDeltaFactor;
  281. } else if (event.deltaMode === 1) {
  282. delta = event.deltaY / deltaYFactor;
  283. } else {
  284. delta = event.deltaY / (10 * deltaYFactor);
  285. }
  286. var x, y;
  287. if (scope.dollyToCursor) {
  288. elementRect = scope._getClientRect(_v4);
  289. x = (event.clientX - elementRect.x) / elementRect.z * 2 - 1;
  290. y = (event.clientY - elementRect.y) / elementRect.w * -2 + 1;
  291. }
  292. dollyInternal(-delta, x, y);
  293. };
  294. var onContextMenu = function onContextMenu(event) {
  295. if (!scope.enabled) return;
  296. event.preventDefault();
  297. };
  298. var startDragging = function startDragging(event) {
  299. if (!scope.enabled) return;
  300. event.preventDefault();
  301. extractClientCoordFromEvent(event, _v2);
  302. elementRect = scope._getClientRect(_v4);
  303. dragStart.copy(_v2);
  304. if (scope._state === STATE.TOUCH_DOLLY_TRUCK) {
  305. // 2 finger pinch
  306. var dx = _v2.x - event.touches[1].clientX;
  307. var dy = _v2.y - event.touches[1].clientY;
  308. var distance = Math.sqrt(dx * dx + dy * dy);
  309. dollyStart.set(0, distance); // center coords of 2 finger truck
  310. var x = (event.touches[0].clientX + event.touches[1].clientX) * 0.5;
  311. var y = (event.touches[0].clientY + event.touches[1].clientY) * 0.5;
  312. dragStart.set(x, y);
  313. }
  314. document.addEventListener('mousemove', dragging, {
  315. passive: false
  316. });
  317. document.addEventListener('touchmove', dragging, {
  318. passive: false
  319. });
  320. document.addEventListener('mouseup', endDragging);
  321. document.addEventListener('touchend', endDragging);
  322. scope.dispatchEvent({
  323. type: 'controlstart',
  324. originalEvent: event
  325. });
  326. };
  327. var dragging = function dragging(event) {
  328. if (!scope.enabled) return;
  329. event.preventDefault();
  330. extractClientCoordFromEvent(event, _v2);
  331. var deltaX = dragStart.x - _v2.x;
  332. var deltaY = dragStart.y - _v2.y;
  333. dragStart.copy(_v2);
  334. switch (scope._state) {
  335. case STATE.ROTATE:
  336. case STATE.TOUCH_ROTATE:
  337. var theta = PI_2 * scope.azimuthRotateSpeed * deltaX / elementRect.z;
  338. var phi = PI_2 * scope.polarRotateSpeed * deltaY / elementRect.w;
  339. scope.rotate(theta, phi, true);
  340. break;
  341. case STATE.DOLLY:
  342. // not implemented
  343. break;
  344. case STATE.TOUCH_DOLLY_TRUCK:
  345. var dx = _v2.x - event.touches[1].clientX;
  346. var dy = _v2.y - event.touches[1].clientY;
  347. var distance = Math.sqrt(dx * dx + dy * dy);
  348. var dollyDelta = dollyStart.y - distance;
  349. var touchDollyFactor = 8;
  350. var dollyX = scope.dollyToCursor ? (dragStart.x - elementRect.x) / elementRect.z * 2 - 1 : 0;
  351. var dollyY = scope.dollyToCursor ? (dragStart.y - elementRect.y) / elementRect.w * -2 + 1 : 0;
  352. dollyInternal(dollyDelta / touchDollyFactor, dollyX, dollyY);
  353. dollyStart.set(0, distance);
  354. truckInternal(deltaX, deltaY);
  355. break;
  356. case STATE.TRUCK:
  357. case STATE.TOUCH_TRUCK:
  358. truckInternal(deltaX, deltaY);
  359. break;
  360. }
  361. scope.dispatchEvent({
  362. type: 'control',
  363. originalEvent: event
  364. });
  365. };
  366. var endDragging = function endDragging(event) {
  367. if (!scope.enabled) return;
  368. scope._state = STATE.NONE;
  369. document.removeEventListener('mousemove', dragging, {
  370. passive: false
  371. });
  372. document.removeEventListener('touchmove', dragging, {
  373. passive: false
  374. });
  375. document.removeEventListener('mouseup', endDragging);
  376. document.removeEventListener('touchend', endDragging);
  377. scope.dispatchEvent({
  378. type: 'controlend',
  379. originalEvent: event
  380. });
  381. };
  382. var truckInternal = function truckInternal(deltaX, deltaY) {
  383. if (scope._camera.isPerspectiveCamera) {
  384. var offset = _v3A.copy(scope._camera.position).sub(scope._target); // half of the fov is center to top of screen
  385. var fovInRad = scope._camera.fov * THREE.Math.DEG2RAD;
  386. var targetDistance = offset.length() * Math.tan(fovInRad / 2);
  387. var truckX = scope.truckSpeed * deltaX * targetDistance / elementRect.w;
  388. var pedestalY = scope.truckSpeed * deltaY * targetDistance / elementRect.w;
  389. if (scope.verticalDragToForward) {
  390. scope.truck(truckX, 0, true);
  391. scope.forward(-pedestalY, true);
  392. } else {
  393. scope.truck(truckX, pedestalY, true);
  394. }
  395. } else if (scope._camera.isOrthographicCamera) {
  396. // orthographic
  397. var _truckX = deltaX * (scope._camera.right - scope._camera.left) / scope._camera.zoom / elementRect.z;
  398. var _pedestalY = deltaY * (scope._camera.top - scope._camera.bottom) / scope._camera.zoom / elementRect.w;
  399. scope.truck(_truckX, _pedestalY, true);
  400. }
  401. };
  402. var dollyInternal = function dollyInternal(delta, x, y) {
  403. var dollyScale = Math.pow(0.95, -delta * scope.dollySpeed);
  404. if (scope._camera.isPerspectiveCamera) {
  405. var distance = scope._sphericalEnd.radius * dollyScale - scope._sphericalEnd.radius;
  406. var prevRadius = scope._sphericalEnd.radius;
  407. scope.dolly(distance);
  408. if (scope.dollyToCursor) {
  409. scope._dollyControlAmount += scope._sphericalEnd.radius - prevRadius;
  410. scope._dollyControlCoord.set(x, y);
  411. }
  412. } else if (scope._camera.isOrthographicCamera) {
  413. scope._camera.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope._camera.zoom * dollyScale));
  414. scope._camera.updateProjectionMatrix();
  415. scope._hasUpdated = true;
  416. }
  417. };
  418. var scope = _assertThisInitialized(_this);
  419. var dragStart = new THREE.Vector2();
  420. var dollyStart = new THREE.Vector2();
  421. var elementRect;
  422. _this._domElement.addEventListener('mousedown', onMouseDown);
  423. _this._domElement.addEventListener('touchstart', onTouchStart);
  424. _this._domElement.addEventListener('wheel', onMouseWheel);
  425. _this._domElement.addEventListener('contextmenu', onContextMenu);
  426. _this._removeAllEventListeners = function () {
  427. scope._domElement.removeEventListener('mousedown', onMouseDown);
  428. scope._domElement.removeEventListener('touchstart', onTouchStart);
  429. scope._domElement.removeEventListener('wheel', onMouseWheel);
  430. scope._domElement.removeEventListener('contextmenu', onContextMenu);
  431. document.removeEventListener('mousemove', dragging);
  432. document.removeEventListener('touchmove', dragging);
  433. document.removeEventListener('mouseup', endDragging);
  434. document.removeEventListener('touchend', endDragging);
  435. };
  436. }
  437. return _this;
  438. } // wrong. phi should be map to polar, but backward compatibility.
  439. _createClass(CameraControls, [{
  440. key: "rotate",
  441. // azimuthAngle in radian
  442. // polarAngle in radian
  443. value: function rotate(azimuthAngle, polarAngle, enableTransition) {
  444. this.rotateTo(this._sphericalEnd.theta + azimuthAngle, this._sphericalEnd.phi + polarAngle, enableTransition);
  445. } // azimuthAngle in radian
  446. // polarAngle in radian
  447. }, {
  448. key: "rotateTo",
  449. value: function rotateTo(azimuthAngle, polarAngle, enableTransition) {
  450. var theta = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, azimuthAngle));
  451. var phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, polarAngle));
  452. this._sphericalEnd.theta = theta;
  453. this._sphericalEnd.phi = phi;
  454. this._sphericalEnd.makeSafe();
  455. if (!enableTransition) {
  456. this._spherical.theta = this._sphericalEnd.theta;
  457. this._spherical.phi = this._sphericalEnd.phi;
  458. }
  459. this._hasUpdated = true;
  460. }
  461. }, {
  462. key: "dolly",
  463. value: function dolly(distance, enableTransition) {
  464. if (this._camera.isOrthographicCamera) {
  465. console.warn('dolly is not available for OrthographicCamera');
  466. return;
  467. }
  468. this.dollyTo(this._sphericalEnd.radius + distance, enableTransition);
  469. }
  470. }, {
  471. key: "dollyTo",
  472. value: function dollyTo(distance, enableTransition) {
  473. if (this._camera.isOrthographicCamera) {
  474. console.warn('dolly is not available for OrthographicCamera');
  475. return;
  476. }
  477. this._sphericalEnd.radius = THREE.Math.clamp(distance, this.minDistance, this.maxDistance);
  478. if (!enableTransition) {
  479. this._spherical.radius = this._sphericalEnd.radius;
  480. }
  481. this._hasUpdated = true;
  482. }
  483. }, {
  484. key: "pan",
  485. value: function pan(x, y, enableTransition) {
  486. console.log('`pan` has been renamed to `truck`');
  487. this.truck(x, y, enableTransition);
  488. }
  489. }, {
  490. key: "truck",
  491. value: function truck(x, y, enableTransition) {
  492. this._camera.updateMatrix();
  493. _xColumn.setFromMatrixColumn(this._camera.matrix, 0);
  494. _yColumn.setFromMatrixColumn(this._camera.matrix, 1);
  495. _xColumn.multiplyScalar(x);
  496. _yColumn.multiplyScalar(-y);
  497. var offset = _v3A.copy(_xColumn).add(_yColumn);
  498. this._encloseToBoundary(this._targetEnd, offset, this.boundaryFriction);
  499. if (!enableTransition) {
  500. this._target.copy(this._targetEnd);
  501. }
  502. this._hasUpdated = true;
  503. }
  504. }, {
  505. key: "forward",
  506. value: function forward(distance, enableTransition) {
  507. _v3A.setFromMatrixColumn(this._camera.matrix, 0);
  508. _v3A.crossVectors(this._camera.up, _v3A);
  509. _v3A.multiplyScalar(distance);
  510. this._encloseToBoundary(this._targetEnd, _v3A, this.boundaryFriction);
  511. if (!enableTransition) {
  512. this._target.copy(this._targetEnd);
  513. }
  514. this._hasUpdated = true;
  515. }
  516. }, {
  517. key: "moveTo",
  518. value: function moveTo(x, y, z, enableTransition) {
  519. this._targetEnd.set(x, y, z);
  520. if (!enableTransition) {
  521. this._target.copy(this._targetEnd);
  522. }
  523. this._hasUpdated = true;
  524. }
  525. }, {
  526. key: "fitTo",
  527. value: function fitTo(box3OrObject, enableTransition) {
  528. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  529. if (this._camera.isOrthographicCamera) {
  530. console.warn('fitTo is not supported for OrthographicCamera');
  531. return;
  532. }
  533. var paddingLeft = options.paddingLeft || 0;
  534. var paddingRight = options.paddingRight || 0;
  535. var paddingBottom = options.paddingBottom || 0;
  536. var paddingTop = options.paddingTop || 0;
  537. var boundingBox = box3OrObject.isBox3 ? box3OrObject.clone() : new THREE.Box3().setFromObject(box3OrObject);
  538. var size = boundingBox.getSize(_v3A);
  539. var boundingWidth = size.x + paddingLeft + paddingRight;
  540. var boundingHeight = size.y + paddingTop + paddingBottom;
  541. var boundingDepth = size.z;
  542. var distance = this.getDistanceToFit(boundingWidth, boundingHeight, boundingDepth);
  543. this.dollyTo(distance, enableTransition);
  544. var boundingBoxCenter = boundingBox.getCenter(_v3A);
  545. var cx = boundingBoxCenter.x - (paddingLeft * 0.5 - paddingRight * 0.5);
  546. var cy = boundingBoxCenter.y + (paddingTop * 0.5 - paddingBottom * 0.5);
  547. var cz = boundingBoxCenter.z;
  548. this.moveTo(cx, cy, cz, enableTransition);
  549. this._sanitizeSphericals();
  550. this.rotateTo(0, 90 * THREE.Math.DEG2RAD, enableTransition);
  551. }
  552. }, {
  553. key: "setLookAt",
  554. value: function setLookAt(positionX, positionY, positionZ, targetX, targetY, targetZ, enableTransition) {
  555. var position = _v3A.set(positionX, positionY, positionZ);
  556. var target = _v3B.set(targetX, targetY, targetZ);
  557. this._targetEnd.copy(target);
  558. this._sphericalEnd.setFromVector3(position.sub(target).applyQuaternion(this._yAxisUpSpace));
  559. this._sanitizeSphericals();
  560. if (!enableTransition) {
  561. this._target.copy(this._targetEnd);
  562. this._spherical.copy(this._sphericalEnd);
  563. }
  564. this._hasUpdated = true;
  565. }
  566. }, {
  567. key: "lerpLookAt",
  568. value: function lerpLookAt(positionAX, positionAY, positionAZ, targetAX, targetAY, targetAZ, positionBX, positionBY, positionBZ, targetBX, targetBY, targetBZ, t, enableTransition) {
  569. var positionA = _v3A.set(positionAX, positionAY, positionAZ);
  570. var targetA = _v3B.set(targetAX, targetAY, targetAZ);
  571. _sphericalA.setFromVector3(positionA.sub(targetA).applyQuaternion(this._yAxisUpSpace));
  572. var targetB = _v3A.set(targetBX, targetBY, targetBZ);
  573. this._targetEnd.copy(targetA).lerp(targetB, t); // tricky
  574. var positionB = _v3B.set(positionBX, positionBY, positionBZ);
  575. _sphericalB.setFromVector3(positionB.sub(targetB).applyQuaternion(this._yAxisUpSpace));
  576. var deltaTheta = _sphericalB.theta - _sphericalA.theta;
  577. var deltaPhi = _sphericalB.phi - _sphericalA.phi;
  578. var deltaRadius = _sphericalB.radius - _sphericalA.radius;
  579. this._sphericalEnd.set(_sphericalA.radius + deltaRadius * t, _sphericalA.phi + deltaPhi * t, _sphericalA.theta + deltaTheta * t);
  580. this._sanitizeSphericals();
  581. if (!enableTransition) {
  582. this._target.copy(this._targetEnd);
  583. this._spherical.copy(this._sphericalEnd);
  584. }
  585. this._hasUpdated = true;
  586. }
  587. }, {
  588. key: "setPosition",
  589. value: function setPosition(positionX, positionY, positionZ, enableTransition) {
  590. this.setLookAt(positionX, positionY, positionZ, this._targetEnd.x, this._targetEnd.y, this._targetEnd.z, enableTransition);
  591. }
  592. }, {
  593. key: "setTarget",
  594. value: function setTarget(targetX, targetY, targetZ, enableTransition) {
  595. var pos = this.getPosition(_v3A);
  596. this.setLookAt(pos.x, pos.y, pos.z, targetX, targetY, targetZ, enableTransition);
  597. }
  598. }, {
  599. key: "setBoundary",
  600. value: function setBoundary(box3) {
  601. if (!box3) {
  602. this._boundary.min.set(-Infinity, -Infinity, -Infinity);
  603. this._boundary.max.set(Infinity, Infinity, Infinity);
  604. this._hasUpdated = true;
  605. return;
  606. }
  607. this._boundary.copy(box3);
  608. this._boundary.clampPoint(this._targetEnd, this._targetEnd);
  609. this._hasUpdated = true;
  610. }
  611. }, {
  612. key: "setViewport",
  613. value: function setViewport(viewportOrX, y, width, height) {
  614. if (viewportOrX === null) {
  615. // null
  616. this._viewport = null;
  617. return;
  618. }
  619. this._viewport = this._viewport || new THREE.Vector4();
  620. if (typeof viewportOrX === 'number') {
  621. // number
  622. this._viewport.set(viewportOrX, y, width, height);
  623. } else {
  624. // Vector4
  625. this._viewport.copy(viewportOrX);
  626. }
  627. }
  628. }, {
  629. key: "getDistanceToFit",
  630. value: function getDistanceToFit(width, height, depth) {
  631. var camera = this._camera;
  632. var boundingRectAspect = width / height;
  633. var fov = camera.fov * THREE.Math.DEG2RAD;
  634. var aspect = camera.aspect;
  635. var heightToFit = boundingRectAspect < aspect ? height : width / aspect;
  636. return heightToFit * 0.5 / Math.tan(fov * 0.5) + depth * 0.5;
  637. }
  638. }, {
  639. key: "getTarget",
  640. value: function getTarget(out) {
  641. var _out = !!out && out.isVector3 ? out : new THREE.Vector3();
  642. return _out.copy(this._targetEnd);
  643. }
  644. }, {
  645. key: "getPosition",
  646. value: function getPosition(out) {
  647. var _out = !!out && out.isVector3 ? out : new THREE.Vector3();
  648. return _out.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse).add(this._targetEnd);
  649. }
  650. }, {
  651. key: "reset",
  652. value: function reset(enableTransition) {
  653. this.setLookAt(this._position0.x, this._position0.y, this._position0.z, this._target0.x, this._target0.y, this._target0.z, enableTransition);
  654. }
  655. }, {
  656. key: "saveState",
  657. value: function saveState() {
  658. this._target0.copy(this._target);
  659. this._position0.copy(this._camera.position);
  660. this._zoom0 = this._camera.zoom;
  661. }
  662. }, {
  663. key: "updateCameraUp",
  664. value: function updateCameraUp() {
  665. this._yAxisUpSpace.setFromUnitVectors(this._camera.up, _AXIS_Y);
  666. this._yAxisUpSpaceInverse.copy(this._yAxisUpSpace).inverse();
  667. }
  668. }, {
  669. key: "update",
  670. value: function update(delta) {
  671. var currentDampingFactor = this._state === STATE.NONE ? this.dampingFactor : this.draggingDampingFactor;
  672. var lerpRatio = 1.0 - Math.exp(-currentDampingFactor * delta / 0.016);
  673. var deltaTheta = this._sphericalEnd.theta - this._spherical.theta;
  674. var deltaPhi = this._sphericalEnd.phi - this._spherical.phi;
  675. var deltaRadius = this._sphericalEnd.radius - this._spherical.radius;
  676. var deltaTarget = _v3A.subVectors(this._targetEnd, this._target);
  677. 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) {
  678. this._spherical.set(this._spherical.radius + deltaRadius * lerpRatio, this._spherical.phi + deltaPhi * lerpRatio, this._spherical.theta + deltaTheta * lerpRatio);
  679. this._target.add(deltaTarget.multiplyScalar(lerpRatio));
  680. this._hasUpdated = true;
  681. } else {
  682. this._spherical.copy(this._sphericalEnd);
  683. this._target.copy(this._targetEnd);
  684. }
  685. if (this._dollyControlAmount !== 0) {
  686. if (this._camera.isPerspectiveCamera) {
  687. var direction = _v3A.copy(_v3A.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse)).normalize().negate();
  688. var planeX = _v3B.copy(direction).cross(_v3C.copy(this._camera.up)).normalize();
  689. if (planeX.lengthSq() === 0) planeX.x = 1.0;
  690. var planeY = _v3C.crossVectors(planeX, direction);
  691. var worldToScreen = this._sphericalEnd.radius * Math.tan(this._camera.fov * THREE.Math.DEG2RAD * 0.5);
  692. var prevRadius = this._sphericalEnd.radius - this._dollyControlAmount;
  693. var _lerpRatio = (prevRadius - this._sphericalEnd.radius) / this._sphericalEnd.radius;
  694. var cursor = _v3A.copy(this._targetEnd).add(planeX.multiplyScalar(this._dollyControlCoord.x * worldToScreen * this._camera.aspect)).add(planeY.multiplyScalar(this._dollyControlCoord.y * worldToScreen));
  695. this._targetEnd.lerp(cursor, _lerpRatio);
  696. this._target.copy(this._targetEnd);
  697. }
  698. this._dollyControlAmount = 0;
  699. }
  700. this._spherical.makeSafe();
  701. this._camera.position.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse).add(this._target);
  702. this._camera.lookAt(this._target);
  703. if (this._boundaryEnclosesCamera) {
  704. this._encloseToBoundary(this._camera.position.copy(this._target), _v3A.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse), 1.0);
  705. }
  706. var updated = this._hasUpdated;
  707. this._hasUpdated = false;
  708. if (updated) this.dispatchEvent({
  709. type: 'update'
  710. });
  711. return updated;
  712. }
  713. }, {
  714. key: "toJSON",
  715. value: function toJSON() {
  716. return JSON.stringify({
  717. enabled: this.enabled,
  718. minDistance: this.minDistance,
  719. maxDistance: infinityToMaxNumber(this.maxDistance),
  720. minPolarAngle: this.minPolarAngle,
  721. maxPolarAngle: infinityToMaxNumber(this.maxPolarAngle),
  722. minAzimuthAngle: infinityToMaxNumber(this.minAzimuthAngle),
  723. maxAzimuthAngle: infinityToMaxNumber(this.maxAzimuthAngle),
  724. dampingFactor: this.dampingFactor,
  725. draggingDampingFactor: this.draggingDampingFactor,
  726. dollySpeed: this.dollySpeed,
  727. truckSpeed: this.truckSpeed,
  728. dollyToCursor: this.dollyToCursor,
  729. verticalDragToForward: this.verticalDragToForward,
  730. target: this._targetEnd.toArray(),
  731. position: this._camera.position.toArray(),
  732. target0: this._target0.toArray(),
  733. position0: this._position0.toArray()
  734. });
  735. }
  736. }, {
  737. key: "fromJSON",
  738. value: function fromJSON(json, enableTransition) {
  739. var obj = JSON.parse(json);
  740. var position = new THREE.Vector3().fromArray(obj.position);
  741. this.enabled = obj.enabled;
  742. this.minDistance = obj.minDistance;
  743. this.maxDistance = maxNumberToInfinity(obj.maxDistance);
  744. this.minPolarAngle = obj.minPolarAngle;
  745. this.maxPolarAngle = maxNumberToInfinity(obj.maxPolarAngle);
  746. this.minAzimuthAngle = maxNumberToInfinity(obj.minAzimuthAngle);
  747. this.maxAzimuthAngle = maxNumberToInfinity(obj.maxAzimuthAngle);
  748. this.dampingFactor = obj.dampingFactor;
  749. this.draggingDampingFactor = obj.draggingDampingFactor;
  750. this.dollySpeed = obj.dollySpeed;
  751. this.truckSpeed = obj.truckSpeed;
  752. this.dollyToCursor = obj.dollyToCursor;
  753. this.verticalDragToForward = obj.verticalDragToForward;
  754. this._target0.fromArray(obj.target0);
  755. this._position0.fromArray(obj.position0);
  756. this._targetEnd.fromArray(obj.target);
  757. this._sphericalEnd.setFromVector3(position.sub(this._target0).applyQuaternion(this._yAxisUpSpace));
  758. if (!enableTransition) {
  759. this._target.copy(this._targetEnd);
  760. this._spherical.copy(this._sphericalEnd);
  761. }
  762. this._hasUpdated = true;
  763. }
  764. }, {
  765. key: "dispose",
  766. value: function dispose() {
  767. this._removeAllEventListeners();
  768. }
  769. }, {
  770. key: "_encloseToBoundary",
  771. value: function _encloseToBoundary(position, offset, friction) {
  772. var offsetLength2 = offset.lengthSq();
  773. if (offsetLength2 === 0.0) {
  774. // sanity check
  775. return position;
  776. } // See: https://twitter.com/FMS_Cat/status/1106508958640988161
  777. var newTarget = _v3B.copy(offset).add(position); // target
  778. var clampedTarget = this._boundary.clampPoint(newTarget, _v3C); // clamped target
  779. var deltaClampedTarget = clampedTarget.sub(newTarget); // newTarget -> clampedTarget
  780. var deltaClampedTargetLength2 = deltaClampedTarget.lengthSq(); // squared length of deltaClampedTarget
  781. if (deltaClampedTargetLength2 === 0.0) {
  782. // when the position doesn't have to be clamped
  783. return position.add(offset);
  784. } else if (deltaClampedTargetLength2 === offsetLength2) {
  785. // when the position is completely stuck
  786. return position;
  787. } else if (friction === 0.0) {
  788. return position.add(offset).add(deltaClampedTarget);
  789. } else {
  790. var offsetFactor = 1.0 + friction * deltaClampedTargetLength2 / offset.dot(deltaClampedTarget);
  791. return position.add(_v3B.copy(offset).multiplyScalar(offsetFactor)).add(deltaClampedTarget.multiplyScalar(1.0 - friction));
  792. }
  793. }
  794. }, {
  795. key: "_sanitizeSphericals",
  796. value: function _sanitizeSphericals() {
  797. this._sphericalEnd.theta = this._sphericalEnd.theta % PI_2;
  798. this._spherical.theta += PI_2 * Math.round((this._sphericalEnd.theta - this._spherical.theta) / PI_2);
  799. }
  800. /**
  801. * Get its client rect and package into given `THREE.Vector4` .
  802. */
  803. }, {
  804. key: "_getClientRect",
  805. value: function _getClientRect(target) {
  806. var rect = this._domElement.getBoundingClientRect();
  807. target.x = rect.left;
  808. target.y = rect.top;
  809. if (this._viewport) {
  810. target.x += this._viewport.x;
  811. target.y += rect.height - this._viewport.w - this._viewport.y;
  812. target.z = this._viewport.z;
  813. target.w = this._viewport.w;
  814. } else {
  815. target.z = rect.width;
  816. target.w = rect.height;
  817. }
  818. return target;
  819. }
  820. }, {
  821. key: "phiSpeed",
  822. set: function set(speed) {
  823. console.warn('phiSpeed was renamed. use azimuthRotateSpeed instead');
  824. this.azimuthRotateSpeed = speed;
  825. } // wrong. theta should be map to azimuth, but backward compatibility.
  826. }, {
  827. key: "thetaSpeed",
  828. set: function set(speed) {
  829. console.warn('thetaSpeed was renamed. use polarRotateSpeed instead');
  830. this.polarRotateSpeed = speed;
  831. }
  832. }, {
  833. key: "boundaryEnclosesCamera",
  834. get: function get() {
  835. return this._boundaryEnclosesCamera;
  836. },
  837. set: function set(boundaryEnclosesCamera) {
  838. this._boundaryEnclosesCamera = boundaryEnclosesCamera;
  839. this._hasUpdated = true;
  840. }
  841. }]);
  842. return CameraControls;
  843. }(EventDispatcher);
  844. function isTouchEvent(event) {
  845. return 'TouchEvent' in window && event instanceof TouchEvent;
  846. }
  847. function infinityToMaxNumber(value) {
  848. if (isFinite(value)) return value;
  849. if (value < 0) return -Number.MAX_VALUE;
  850. return Number.MAX_VALUE;
  851. }
  852. function maxNumberToInfinity(value) {
  853. if (Math.abs(value) < Number.MAX_VALUE) return value;
  854. return value * Infinity;
  855. }
  856. return CameraControls;
  857. }));