Jelajahi Sumber

Examples: Convert controls to ES6 Part I. (#21629)

* Examples: Convert controls to ES6 Part I.

* Examples: Clean up.
Michael Herzog 4 tahun lalu
induk
melakukan
495d3a0109

+ 74 - 77
examples/js/controls/DeviceOrientationControls.js

@@ -1,128 +1,128 @@
 ( function () {
 
-	/**
- * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
- */
+	const _zee = new THREE.Vector3( 0, 0, 1 );
 
-	var DeviceOrientationControls = function ( object ) {
+	const _euler = new THREE.Euler();
 
-		if ( window.isSecureContext === false ) {
+	const _q0 = new THREE.Quaternion();
 
-			console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
+	const _q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
 
-		}
 
-		var scope = this;
-		var changeEvent = {
-			type: 'change'
-		};
-		var EPS = 0.000001;
-		this.object = object;
-		this.object.rotation.reorder( 'YXZ' );
-		this.enabled = true;
-		this.deviceOrientation = {};
-		this.screenOrientation = 0;
-		this.alphaOffset = 0; // radians
+	const _changeEvent = {
+		type: 'change'
+	};
 
-		var onDeviceOrientationChangeEvent = function ( event ) {
+	class DeviceOrientationControls extends THREE.EventDispatcher {
 
-			scope.deviceOrientation = event;
+		constructor( object ) {
 
-		};
+			super();
 
-		var onScreenOrientationChangeEvent = function () {
+			if ( window.isSecureContext === false ) {
 
-			scope.screenOrientation = window.orientation || 0;
+				console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
 
-		}; // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
+			}
 
+			const scope = this;
+			const EPS = 0.000001;
+			const lastQuaternion = new THREE.Quaternion();
+			this.object = object;
+			this.object.rotation.reorder( 'YXZ' );
+			this.enabled = true;
+			this.deviceOrientation = {};
+			this.screenOrientation = 0;
+			this.alphaOffset = 0; // radians
 
-		var setObjectQuaternion = function () {
+			const onDeviceOrientationChangeEvent = function ( event ) {
 
-			var zee = new THREE.Vector3( 0, 0, 1 );
-			var euler = new THREE.Euler();
-			var q0 = new THREE.Quaternion();
-			var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
+				scope.deviceOrientation = event;
 
-			return function ( quaternion, alpha, beta, gamma, orient ) {
+			};
 
-				euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
+			const onScreenOrientationChangeEvent = function () {
 
-				quaternion.setFromEuler( euler ); // orient the device
+				scope.screenOrientation = window.orientation || 0;
 
-				quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
+			}; // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
 
-				quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
 
-			};
+			const setObjectQuaternion = function ( quaternion, alpha, beta, gamma, orient ) {
 
-		}();
+				_euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
 
-		this.connect = function () {
 
-			onScreenOrientationChangeEvent(); // run once on load
-			// iOS 13+
+				quaternion.setFromEuler( _euler ); // orient the device
 
-			if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
+				quaternion.multiply( _q1 ); // camera looks out the back of the device, not the top
 
-				window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
+				quaternion.multiply( _q0.setFromAxisAngle( _zee, - orient ) ); // adjust for screen orientation
 
-					if ( response == 'granted' ) {
+			};
 
-						window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-						window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
+			this.connect = function () {
 
-					}
+				onScreenOrientationChangeEvent(); // run once on load
+				// iOS 13+
 
-				} ).catch( function ( error ) {
+				if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
 
-					console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
+					window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
 
-				} );
+						if ( response == 'granted' ) {
 
-			} else {
+							window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+							window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
 
-				window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-				window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
+						}
 
-			}
+					} ).catch( function ( error ) {
 
-			scope.enabled = true;
+						console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
 
-		};
+					} );
 
-		this.disconnect = function () {
+				} else {
 
-			window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-			window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-			scope.enabled = false;
+					window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+					window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
 
-		};
+				}
 
-		this.update = function () {
+				scope.enabled = true;
 
-			var lastQuaternion = new THREE.Quaternion();
-			return function () {
+			};
+
+			this.disconnect = function () {
+
+				window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+				window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
+				scope.enabled = false;
+
+			};
+
+			this.update = function () {
 
 				if ( scope.enabled === false ) return;
-				var device = scope.deviceOrientation;
+				const device = scope.deviceOrientation;
 
 				if ( device ) {
 
-					var alpha = device.alpha ? THREE.MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
+					const alpha = device.alpha ? THREE.MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
 
-					var beta = device.beta ? THREE.MathUtils.degToRad( device.beta ) : 0; // X'
+					const beta = device.beta ? THREE.MathUtils.degToRad( device.beta ) : 0; // X'
 
-					var gamma = device.gamma ? THREE.MathUtils.degToRad( device.gamma ) : 0; // Y''
+					const gamma = device.gamma ? THREE.MathUtils.degToRad( device.gamma ) : 0; // Y''
 
-					var orient = scope.screenOrientation ? THREE.MathUtils.degToRad( scope.screenOrientation ) : 0; // O
+					const orient = scope.screenOrientation ? THREE.MathUtils.degToRad( scope.screenOrientation ) : 0; // O
 
 					setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
 
 					if ( 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
 
 						lastQuaternion.copy( scope.object.quaternion );
-						scope.dispatchEvent( changeEvent );
+						scope.dispatchEvent( _changeEvent );
 
 					}
 
@@ -130,20 +130,17 @@
 
 			};
 
-		}();
+			this.dispose = function () {
 
-		this.dispose = function () {
+				scope.disconnect();
 
-			scope.disconnect();
-
-		};
+			};
 
-		this.connect();
+			this.connect();
 
-	};
+		}
 
-	DeviceOrientationControls.prototype = Object.create( THREE.EventDispatcher.prototype );
-	DeviceOrientationControls.prototype.constructor = DeviceOrientationControls;
+	}
 
 	THREE.DeviceOrientationControls = DeviceOrientationControls;
 

+ 200 - 198
examples/js/controls/DragControls.js

@@ -1,350 +1,352 @@
 ( function () {
 
-	var DragControls = function ( _objects, _camera, _domElement ) {
+	const _plane = new THREE.Plane();
 
-		var _plane = new THREE.Plane();
+	const _raycaster = new THREE.Raycaster();
 
-		var _raycaster = new THREE.Raycaster();
+	const _mouse = new THREE.Vector2();
 
-		var _mouse = new THREE.Vector2();
+	const _offset = new THREE.Vector3();
 
-		var _offset = new THREE.Vector3();
+	const _intersection = new THREE.Vector3();
 
-		var _intersection = new THREE.Vector3();
+	const _worldPosition = new THREE.Vector3();
 
-		var _worldPosition = new THREE.Vector3();
+	const _inverseMatrix = new THREE.Matrix4();
 
-		var _inverseMatrix = new THREE.Matrix4();
+	class DragControls extends THREE.EventDispatcher {
 
-		var _intersections = [];
-		var _selected = null,
-			_hovered = null; //
+		constructor( _objects, _camera, _domElement ) {
 
-		var scope = this;
+			super();
+			let _selected = null,
+				_hovered = null;
+			const _intersections = []; //
 
-		function activate() {
+			const scope = this;
 
-			_domElement.addEventListener( 'pointermove', onPointerMove );
+			function activate() {
 
-			_domElement.addEventListener( 'pointerdown', onPointerDown );
+				_domElement.addEventListener( 'pointermove', onPointerMove );
 
-			_domElement.addEventListener( 'pointerup', onPointerCancel );
+				_domElement.addEventListener( 'pointerdown', onPointerDown );
 
-			_domElement.addEventListener( 'pointerleave', onPointerCancel );
+				_domElement.addEventListener( 'pointerup', onPointerCancel );
 
-			_domElement.addEventListener( 'touchmove', onTouchMove );
+				_domElement.addEventListener( 'pointerleave', onPointerCancel );
 
-			_domElement.addEventListener( 'touchstart', onTouchStart );
+				_domElement.addEventListener( 'touchmove', onTouchMove );
 
-			_domElement.addEventListener( 'touchend', onTouchEnd );
+				_domElement.addEventListener( 'touchstart', onTouchStart );
 
-		}
+				_domElement.addEventListener( 'touchend', onTouchEnd );
 
-		function deactivate() {
+			}
 
-			_domElement.removeEventListener( 'pointermove', onPointerMove );
+			function deactivate() {
 
-			_domElement.removeEventListener( 'pointerdown', onPointerDown );
+				_domElement.removeEventListener( 'pointermove', onPointerMove );
 
-			_domElement.removeEventListener( 'pointerup', onPointerCancel );
+				_domElement.removeEventListener( 'pointerdown', onPointerDown );
 
-			_domElement.removeEventListener( 'pointerleave', onPointerCancel );
+				_domElement.removeEventListener( 'pointerup', onPointerCancel );
 
-			_domElement.removeEventListener( 'touchmove', onTouchMove );
+				_domElement.removeEventListener( 'pointerleave', onPointerCancel );
 
-			_domElement.removeEventListener( 'touchstart', onTouchStart );
+				_domElement.removeEventListener( 'touchmove', onTouchMove );
 
-			_domElement.removeEventListener( 'touchend', onTouchEnd );
+				_domElement.removeEventListener( 'touchstart', onTouchStart );
 
-			_domElement.style.cursor = '';
+				_domElement.removeEventListener( 'touchend', onTouchEnd );
 
-		}
+				_domElement.style.cursor = '';
 
-		function dispose() {
+			}
 
-			deactivate();
+			function dispose() {
 
-		}
+				deactivate();
 
-		function getObjects() {
+			}
 
-			return _objects;
+			function getObjects() {
 
-		}
+				return _objects;
 
-		function onPointerMove( event ) {
+			}
 
-			event.preventDefault();
+			function onPointerMove( event ) {
 
-			switch ( event.pointerType ) {
+				event.preventDefault();
 
-				case 'mouse':
-				case 'pen':
-					onMouseMove( event );
-					break;
-			// TODO touch
+				switch ( event.pointerType ) {
+
+					case 'mouse':
+					case 'pen':
+						onMouseMove( event );
+						break;
+				// TODO touch
+
+				}
 
 			}
 
-		}
+			function onMouseMove( event ) {
 
-		function onMouseMove( event ) {
+				const rect = _domElement.getBoundingClientRect();
 
-			var rect = _domElement.getBoundingClientRect();
+				_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+				_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
-			_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+				_raycaster.setFromCamera( _mouse, _camera );
 
-			_raycaster.setFromCamera( _mouse, _camera );
+				if ( _selected && scope.enabled ) {
 
-			if ( _selected && scope.enabled ) {
+					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+						_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
 
-					_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+					}
 
-				}
+					scope.dispatchEvent( {
+						type: 'drag',
+						object: _selected
+					} );
+					return;
 
-				scope.dispatchEvent( {
-					type: 'drag',
-					object: _selected
-				} );
-				return;
+				}
 
-			}
+				_intersections.length = 0;
 
-			_intersections.length = 0;
+				_raycaster.setFromCamera( _mouse, _camera );
 
-			_raycaster.setFromCamera( _mouse, _camera );
+				_raycaster.intersectObjects( _objects, true, _intersections );
 
-			_raycaster.intersectObjects( _objects, true, _intersections );
+				if ( _intersections.length > 0 ) {
 
-			if ( _intersections.length > 0 ) {
+					const object = _intersections[ 0 ].object;
 
-				var object = _intersections[ 0 ].object;
+					_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
 
-				_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
+					if ( _hovered !== object && _hovered !== null ) {
 
-				if ( _hovered !== object && _hovered !== null ) {
+						scope.dispatchEvent( {
+							type: 'hoveroff',
+							object: _hovered
+						} );
+						_domElement.style.cursor = 'auto';
+						_hovered = null;
 
-					scope.dispatchEvent( {
-						type: 'hoveroff',
-						object: _hovered
-					} );
-					_domElement.style.cursor = 'auto';
-					_hovered = null;
+					}
 
-				}
+					if ( _hovered !== object ) {
 
-				if ( _hovered !== object ) {
+						scope.dispatchEvent( {
+							type: 'hoveron',
+							object: object
+						} );
+						_domElement.style.cursor = 'pointer';
+						_hovered = object;
 
-					scope.dispatchEvent( {
-						type: 'hoveron',
-						object: object
-					} );
-					_domElement.style.cursor = 'pointer';
-					_hovered = object;
+					}
 
-				}
+				} else {
 
-			} else {
+					if ( _hovered !== null ) {
 
-				if ( _hovered !== null ) {
+						scope.dispatchEvent( {
+							type: 'hoveroff',
+							object: _hovered
+						} );
+						_domElement.style.cursor = 'auto';
+						_hovered = null;
 
-					scope.dispatchEvent( {
-						type: 'hoveroff',
-						object: _hovered
-					} );
-					_domElement.style.cursor = 'auto';
-					_hovered = null;
+					}
 
 				}
 
 			}
 
-		}
+			function onPointerDown( event ) {
 
-		function onPointerDown( event ) {
+				event.preventDefault();
 
-			event.preventDefault();
+				switch ( event.pointerType ) {
 
-			switch ( event.pointerType ) {
+					case 'mouse':
+					case 'pen':
+						onMouseDown( event );
+						break;
+				// TODO touch
 
-				case 'mouse':
-				case 'pen':
-					onMouseDown( event );
-					break;
-			// TODO touch
+				}
 
 			}
 
-		}
+			function onMouseDown( event ) {
 
-		function onMouseDown( event ) {
+				event.preventDefault();
+				_intersections.length = 0;
 
-			event.preventDefault();
-			_intersections.length = 0;
+				_raycaster.setFromCamera( _mouse, _camera );
 
-			_raycaster.setFromCamera( _mouse, _camera );
+				_raycaster.intersectObjects( _objects, true, _intersections );
 
-			_raycaster.intersectObjects( _objects, true, _intersections );
+				if ( _intersections.length > 0 ) {
 
-			if ( _intersections.length > 0 ) {
+					_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
 
-				_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
+					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+						_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
 
-					_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
+						_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-					_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+					}
 
-				}
+					_domElement.style.cursor = 'move';
+					scope.dispatchEvent( {
+						type: 'dragstart',
+						object: _selected
+					} );
 
-				_domElement.style.cursor = 'move';
-				scope.dispatchEvent( {
-					type: 'dragstart',
-					object: _selected
-				} );
+				}
 
 			}
 
-		}
+			function onPointerCancel( event ) {
 
-		function onPointerCancel( event ) {
+				event.preventDefault();
 
-			event.preventDefault();
+				switch ( event.pointerType ) {
 
-			switch ( event.pointerType ) {
+					case 'mouse':
+					case 'pen':
+						onMouseCancel( event );
+						break;
+				// TODO touch
 
-				case 'mouse':
-				case 'pen':
-					onMouseCancel( event );
-					break;
-			// TODO touch
+				}
 
 			}
 
-		}
+			function onMouseCancel( event ) {
+
+				event.preventDefault();
 
-		function onMouseCancel( event ) {
+				if ( _selected ) {
 
-			event.preventDefault();
+					scope.dispatchEvent( {
+						type: 'dragend',
+						object: _selected
+					} );
+					_selected = null;
 
-			if ( _selected ) {
+				}
 
-				scope.dispatchEvent( {
-					type: 'dragend',
-					object: _selected
-				} );
-				_selected = null;
+				_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
 
 			}
 
-			_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
+			function onTouchMove( event ) {
 
-		}
+				event.preventDefault();
+				event = event.changedTouches[ 0 ];
 
-		function onTouchMove( event ) {
+				const rect = _domElement.getBoundingClientRect();
 
-			event.preventDefault();
-			event = event.changedTouches[ 0 ];
+				_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+				_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
-			var rect = _domElement.getBoundingClientRect();
+				_raycaster.setFromCamera( _mouse, _camera );
 
-			_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+				if ( _selected && scope.enabled ) {
 
-			_raycaster.setFromCamera( _mouse, _camera );
+					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-			if ( _selected && scope.enabled ) {
+						_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
 
-				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+					}
 
-					_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+					scope.dispatchEvent( {
+						type: 'drag',
+						object: _selected
+					} );
+					return;
 
 				}
 
-				scope.dispatchEvent( {
-					type: 'drag',
-					object: _selected
-				} );
-				return;
-
 			}
 
-		}
+			function onTouchStart( event ) {
 
-		function onTouchStart( event ) {
+				event.preventDefault();
+				event = event.changedTouches[ 0 ];
 
-			event.preventDefault();
-			event = event.changedTouches[ 0 ];
+				const rect = _domElement.getBoundingClientRect();
 
-			var rect = _domElement.getBoundingClientRect();
+				_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+				_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+				_intersections.length = 0;
 
-			_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
-			_intersections.length = 0;
+				_raycaster.setFromCamera( _mouse, _camera );
 
-			_raycaster.setFromCamera( _mouse, _camera );
+				_raycaster.intersectObjects( _objects, true, _intersections );
 
-			_raycaster.intersectObjects( _objects, true, _intersections );
+				if ( _intersections.length > 0 ) {
 
-			if ( _intersections.length > 0 ) {
+					_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
 
-				_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
+					_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-				_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+						_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
 
-					_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
+						_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-					_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+					}
 
-				}
+					_domElement.style.cursor = 'move';
+					scope.dispatchEvent( {
+						type: 'dragstart',
+						object: _selected
+					} );
 
-				_domElement.style.cursor = 'move';
-				scope.dispatchEvent( {
-					type: 'dragstart',
-					object: _selected
-				} );
+				}
 
 			}
 
-		}
-
-		function onTouchEnd( event ) {
+			function onTouchEnd( event ) {
 
-			event.preventDefault();
+				event.preventDefault();
 
-			if ( _selected ) {
+				if ( _selected ) {
 
-				scope.dispatchEvent( {
-					type: 'dragend',
-					object: _selected
-				} );
-				_selected = null;
+					scope.dispatchEvent( {
+						type: 'dragend',
+						object: _selected
+					} );
+					_selected = null;
 
-			}
+				}
 
-			_domElement.style.cursor = 'auto';
+				_domElement.style.cursor = 'auto';
 
-		}
+			}
 
-		activate(); // API
+			activate(); // API
 
-		this.enabled = true;
-		this.transformGroup = false;
-		this.activate = activate;
-		this.deactivate = deactivate;
-		this.dispose = dispose;
-		this.getObjects = getObjects;
+			this.enabled = true;
+			this.transformGroup = false;
+			this.activate = activate;
+			this.deactivate = deactivate;
+			this.dispose = dispose;
+			this.getObjects = getObjects;
 
-	};
+		}
 
-	DragControls.prototype = Object.create( THREE.EventDispatcher.prototype );
-	DragControls.prototype.constructor = DragControls;
+	}
 
 	THREE.DragControls = DragControls;
 

+ 223 - 230
examples/js/controls/FirstPersonControls.js

@@ -1,352 +1,345 @@
 ( function () {
 
-	var FirstPersonControls = function ( object, domElement ) {
+	const _lookDirection = new THREE.Vector3();
 
-		if ( domElement === undefined ) {
+	const _spherical = new THREE.Spherical();
 
-			console.warn( 'THREE.FirstPersonControls: The second parameter "domElement" is now mandatory.' );
-			domElement = document;
+	const _target = new THREE.Vector3();
 
-		}
-
-		this.object = object;
-		this.domElement = domElement; // API
-
-		this.enabled = true;
-		this.movementSpeed = 1.0;
-		this.lookSpeed = 0.005;
-		this.lookVertical = true;
-		this.autoForward = false;
-		this.activeLook = true;
-		this.heightSpeed = false;
-		this.heightCoef = 1.0;
-		this.heightMin = 0.0;
-		this.heightMax = 1.0;
-		this.constrainVertical = false;
-		this.verticalMin = 0;
-		this.verticalMax = Math.PI;
-		this.mouseDragOn = false; // internals
-
-		this.autoSpeedFactor = 0.0;
-		this.mouseX = 0;
-		this.mouseY = 0;
-		this.moveForward = false;
-		this.moveBackward = false;
-		this.moveLeft = false;
-		this.moveRight = false;
-		this.viewHalfX = 0;
-		this.viewHalfY = 0; // private variables
-
-		var lat = 0;
-		var lon = 0;
-		var lookDirection = new THREE.Vector3();
-		var spherical = new THREE.Spherical();
-		var target = new THREE.Vector3(); //
+	class FirstPersonControls {
 
-		if ( this.domElement !== document ) {
+		constructor( object, domElement ) {
 
-			this.domElement.setAttribute( 'tabindex', - 1 );
+			if ( domElement === undefined ) {
 
-		} //
+				console.warn( 'THREE.FirstPersonControls: The second parameter "domElement" is now mandatory.' );
+				domElement = document;
 
+			}
 
-		this.handleResize = function () {
-
-			if ( this.domElement === document ) {
+			this.object = object;
+			this.domElement = domElement; // API
+
+			this.enabled = true;
+			this.movementSpeed = 1.0;
+			this.lookSpeed = 0.005;
+			this.lookVertical = true;
+			this.autoForward = false;
+			this.activeLook = true;
+			this.heightSpeed = false;
+			this.heightCoef = 1.0;
+			this.heightMin = 0.0;
+			this.heightMax = 1.0;
+			this.constrainVertical = false;
+			this.verticalMin = 0;
+			this.verticalMax = Math.PI;
+			this.mouseDragOn = false; // internals
+
+			this.autoSpeedFactor = 0.0;
+			this.mouseX = 0;
+			this.mouseY = 0;
+			this.moveForward = false;
+			this.moveBackward = false;
+			this.moveLeft = false;
+			this.moveRight = false;
+			this.viewHalfX = 0;
+			this.viewHalfY = 0; // private variables
+
+			let lat = 0;
+			let lon = 0; //
+
+			this.handleResize = function () {
+
+				if ( this.domElement === document ) {
+
+					this.viewHalfX = window.innerWidth / 2;
+					this.viewHalfY = window.innerHeight / 2;
 
-				this.viewHalfX = window.innerWidth / 2;
-				this.viewHalfY = window.innerHeight / 2;
+				} else {
 
-			} else {
+					this.viewHalfX = this.domElement.offsetWidth / 2;
+					this.viewHalfY = this.domElement.offsetHeight / 2;
 
-				this.viewHalfX = this.domElement.offsetWidth / 2;
-				this.viewHalfY = this.domElement.offsetHeight / 2;
+				}
 
-			}
+			};
 
-		};
+			this.onMouseDown = function ( event ) {
 
-		this.onMouseDown = function ( event ) {
+				if ( this.domElement !== document ) {
 
-			if ( this.domElement !== document ) {
+					this.domElement.focus();
 
-				this.domElement.focus();
+				}
 
-			}
+				event.preventDefault();
 
-			event.preventDefault();
+				if ( this.activeLook ) {
 
-			if ( this.activeLook ) {
+					switch ( event.button ) {
 
-				switch ( event.button ) {
+						case 0:
+							this.moveForward = true;
+							break;
 
-					case 0:
-						this.moveForward = true;
-						break;
+						case 2:
+							this.moveBackward = true;
+							break;
 
-					case 2:
-						this.moveBackward = true;
-						break;
+					}
 
 				}
 
-			}
+				this.mouseDragOn = true;
 
-			this.mouseDragOn = true;
+			};
 
-		};
+			this.onMouseUp = function ( event ) {
 
-		this.onMouseUp = function ( event ) {
+				event.preventDefault();
 
-			event.preventDefault();
+				if ( this.activeLook ) {
 
-			if ( this.activeLook ) {
+					switch ( event.button ) {
 
-				switch ( event.button ) {
+						case 0:
+							this.moveForward = false;
+							break;
 
-					case 0:
-						this.moveForward = false;
-						break;
+						case 2:
+							this.moveBackward = false;
+							break;
 
-					case 2:
-						this.moveBackward = false;
-						break;
+					}
 
 				}
 
-			}
-
-			this.mouseDragOn = false;
+				this.mouseDragOn = false;
 
-		};
+			};
 
-		this.onMouseMove = function ( event ) {
+			this.onMouseMove = function ( event ) {
 
-			if ( this.domElement === document ) {
+				if ( this.domElement === document ) {
 
-				this.mouseX = event.pageX - this.viewHalfX;
-				this.mouseY = event.pageY - this.viewHalfY;
+					this.mouseX = event.pageX - this.viewHalfX;
+					this.mouseY = event.pageY - this.viewHalfY;
 
-			} else {
+				} else {
 
-				this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
-				this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
+					this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
+					this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
 
-			}
+				}
 
-		};
+			};
 
-		this.onKeyDown = function ( event ) {
+			this.onKeyDown = function ( event ) {
 
-			//event.preventDefault();
-			switch ( event.code ) {
+				//event.preventDefault();
+				switch ( event.code ) {
 
-				case 'ArrowUp':
-				case 'KeyW':
-					this.moveForward = true;
-					break;
+					case 'ArrowUp':
+					case 'KeyW':
+						this.moveForward = true;
+						break;
 
-				case 'ArrowLeft':
-				case 'KeyA':
-					this.moveLeft = true;
-					break;
+					case 'ArrowLeft':
+					case 'KeyA':
+						this.moveLeft = true;
+						break;
 
-				case 'ArrowDown':
-				case 'KeyS':
-					this.moveBackward = true;
-					break;
+					case 'ArrowDown':
+					case 'KeyS':
+						this.moveBackward = true;
+						break;
 
-				case 'ArrowRight':
-				case 'KeyD':
-					this.moveRight = true;
-					break;
+					case 'ArrowRight':
+					case 'KeyD':
+						this.moveRight = true;
+						break;
 
-				case 'KeyR':
-					this.moveUp = true;
-					break;
+					case 'KeyR':
+						this.moveUp = true;
+						break;
 
-				case 'KeyF':
-					this.moveDown = true;
-					break;
+					case 'KeyF':
+						this.moveDown = true;
+						break;
 
-			}
+				}
 
-		};
+			};
 
-		this.onKeyUp = function ( event ) {
+			this.onKeyUp = function ( event ) {
 
-			switch ( event.code ) {
+				switch ( event.code ) {
 
-				case 'ArrowUp':
-				case 'KeyW':
-					this.moveForward = false;
-					break;
+					case 'ArrowUp':
+					case 'KeyW':
+						this.moveForward = false;
+						break;
 
-				case 'ArrowLeft':
-				case 'KeyA':
-					this.moveLeft = false;
-					break;
+					case 'ArrowLeft':
+					case 'KeyA':
+						this.moveLeft = false;
+						break;
 
-				case 'ArrowDown':
-				case 'KeyS':
-					this.moveBackward = false;
-					break;
+					case 'ArrowDown':
+					case 'KeyS':
+						this.moveBackward = false;
+						break;
 
-				case 'ArrowRight':
-				case 'KeyD':
-					this.moveRight = false;
-					break;
+					case 'ArrowRight':
+					case 'KeyD':
+						this.moveRight = false;
+						break;
 
-				case 'KeyR':
-					this.moveUp = false;
-					break;
+					case 'KeyR':
+						this.moveUp = false;
+						break;
 
-				case 'KeyF':
-					this.moveDown = false;
-					break;
+					case 'KeyF':
+						this.moveDown = false;
+						break;
 
-			}
+				}
 
-		};
+			};
 
-		this.lookAt = function ( x, y, z ) {
+			this.lookAt = function ( x, y, z ) {
 
-			if ( x.isVector3 ) {
+				if ( x.isVector3 ) {
 
-				target.copy( x );
+					_target.copy( x );
 
-			} else {
+				} else {
 
-				target.set( x, y, z );
+					_target.set( x, y, z );
 
-			}
+				}
 
-			this.object.lookAt( target );
-			setOrientation( this );
-			return this;
+				this.object.lookAt( _target );
+				setOrientation( this );
+				return this;
 
-		};
+			};
 
-		this.update = function () {
+			this.update = function () {
 
-			var targetPosition = new THREE.Vector3();
-			return function update( delta ) {
+				const targetPosition = new THREE.Vector3();
+				return function update( delta ) {
 
-				if ( this.enabled === false ) return;
+					if ( this.enabled === false ) return;
 
-				if ( this.heightSpeed ) {
+					if ( this.heightSpeed ) {
 
-					var y = THREE.MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
-					var heightDelta = y - this.heightMin;
-					this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
+						const y = THREE.MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
+						const heightDelta = y - this.heightMin;
+						this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
 
-				} else {
+					} else {
 
-					this.autoSpeedFactor = 0.0;
+						this.autoSpeedFactor = 0.0;
 
-				}
+					}
 
-				var actualMoveSpeed = delta * this.movementSpeed;
-				if ( this.moveForward || this.autoForward && ! this.moveBackward ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
-				if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
-				if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
-				if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
-				if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
-				if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
-				var actualLookSpeed = delta * this.lookSpeed;
+					const actualMoveSpeed = delta * this.movementSpeed;
+					if ( this.moveForward || this.autoForward && ! this.moveBackward ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
+					if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
+					if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
+					if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
+					if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
+					if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
+					let actualLookSpeed = delta * this.lookSpeed;
 
-				if ( ! this.activeLook ) {
+					if ( ! this.activeLook ) {
 
-					actualLookSpeed = 0;
+						actualLookSpeed = 0;
 
-				}
+					}
 
-				var verticalLookRatio = 1;
+					let verticalLookRatio = 1;
 
-				if ( this.constrainVertical ) {
+					if ( this.constrainVertical ) {
 
-					verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
+						verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
 
-				}
+					}
 
-				lon -= this.mouseX * actualLookSpeed;
-				if ( this.lookVertical ) lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
-				lat = Math.max( - 85, Math.min( 85, lat ) );
-				var phi = THREE.MathUtils.degToRad( 90 - lat );
-				var theta = THREE.MathUtils.degToRad( lon );
+					lon -= this.mouseX * actualLookSpeed;
+					if ( this.lookVertical ) lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
+					lat = Math.max( - 85, Math.min( 85, lat ) );
+					let phi = THREE.MathUtils.degToRad( 90 - lat );
+					const theta = THREE.MathUtils.degToRad( lon );
 
-				if ( this.constrainVertical ) {
+					if ( this.constrainVertical ) {
 
-					phi = THREE.MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
+						phi = THREE.MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
 
-				}
+					}
 
-				var position = this.object.position;
-				targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
-				this.object.lookAt( targetPosition );
+					const position = this.object.position;
+					targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
+					this.object.lookAt( targetPosition );
 
-			};
+				};
 
-		}();
+			}();
 
-		function contextmenu( event ) {
+			this.dispose = function () {
 
-			event.preventDefault();
+				this.domElement.removeEventListener( 'contextmenu', contextmenu );
+				this.domElement.removeEventListener( 'mousedown', _onMouseDown );
+				this.domElement.removeEventListener( 'mousemove', _onMouseMove );
+				this.domElement.removeEventListener( 'mouseup', _onMouseUp );
+				window.removeEventListener( 'keydown', _onKeyDown );
+				window.removeEventListener( 'keyup', _onKeyUp );
 
-		}
+			};
 
-		this.dispose = function () {
+			const _onMouseMove = this.onMouseMove.bind( this );
 
-			this.domElement.removeEventListener( 'contextmenu', contextmenu );
-			this.domElement.removeEventListener( 'mousedown', _onMouseDown );
-			this.domElement.removeEventListener( 'mousemove', _onMouseMove );
-			this.domElement.removeEventListener( 'mouseup', _onMouseUp );
-			window.removeEventListener( 'keydown', _onKeyDown );
-			window.removeEventListener( 'keyup', _onKeyUp );
+			const _onMouseDown = this.onMouseDown.bind( this );
 
-		};
+			const _onMouseUp = this.onMouseUp.bind( this );
 
-		var _onMouseMove = bind( this, this.onMouseMove );
+			const _onKeyDown = this.onKeyDown.bind( this );
 
-		var _onMouseDown = bind( this, this.onMouseDown );
+			const _onKeyUp = this.onKeyUp.bind( this );
 
-		var _onMouseUp = bind( this, this.onMouseUp );
+			this.domElement.addEventListener( 'contextmenu', contextmenu );
+			this.domElement.addEventListener( 'mousemove', _onMouseMove );
+			this.domElement.addEventListener( 'mousedown', _onMouseDown );
+			this.domElement.addEventListener( 'mouseup', _onMouseUp );
+			window.addEventListener( 'keydown', _onKeyDown );
+			window.addEventListener( 'keyup', _onKeyUp );
 
-		var _onKeyDown = bind( this, this.onKeyDown );
+			function setOrientation( controls ) {
 
-		var _onKeyUp = bind( this, this.onKeyUp );
+				const quaternion = controls.object.quaternion;
 
-		this.domElement.addEventListener( 'contextmenu', contextmenu );
-		this.domElement.addEventListener( 'mousemove', _onMouseMove );
-		this.domElement.addEventListener( 'mousedown', _onMouseDown );
-		this.domElement.addEventListener( 'mouseup', _onMouseUp );
-		window.addEventListener( 'keydown', _onKeyDown );
-		window.addEventListener( 'keyup', _onKeyUp );
+				_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
 
-		function bind( scope, fn ) {
+				_spherical.setFromVector3( _lookDirection );
 
-			return function () {
+				lat = 90 - THREE.MathUtils.radToDeg( _spherical.phi );
+				lon = THREE.MathUtils.radToDeg( _spherical.theta );
 
-				fn.apply( scope, arguments );
+			}
 
-			};
+			this.handleResize();
+			setOrientation( this );
 
 		}
 
-		function setOrientation( controls ) {
+	}
 
-			var quaternion = controls.object.quaternion;
-			lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
-			spherical.setFromVector3( lookDirection );
-			lat = 90 - THREE.MathUtils.radToDeg( spherical.phi );
-			lon = THREE.MathUtils.radToDeg( spherical.theta );
-
-		}
+	function contextmenu( event ) {
 
-		this.handleResize();
-		setOrientation( this );
+		event.preventDefault();
 
-	};
+	}
 
 	THREE.FirstPersonControls = FirstPersonControls;
 

+ 251 - 262
examples/js/controls/FlyControls.js

@@ -1,271 +1,273 @@
 ( function () {
 
-	var FlyControls = function ( object, domElement ) {
+	const _changeEvent = {
+		type: 'change'
+	};
 
-		if ( domElement === undefined ) {
+	class FlyControls extends THREE.EventDispatcher {
 
-			console.warn( 'THREE.FlyControls: The second parameter "domElement" is now mandatory.' );
-			domElement = document;
+		constructor( object, domElement ) {
 
-		}
+			super();
 
-		this.object = object;
-		this.domElement = domElement;
-		if ( domElement ) this.domElement.setAttribute( 'tabindex', - 1 ); // API
-
-		this.movementSpeed = 1.0;
-		this.rollSpeed = 0.005;
-		this.dragToLook = false;
-		this.autoForward = false; // disable default target object behavior
-		// internals
-
-		var scope = this;
-		var changeEvent = {
-			type: 'change'
-		};
-		var EPS = 0.000001;
-		this.tmpQuaternion = new THREE.Quaternion();
-		this.mouseStatus = 0;
-		this.moveState = {
-			up: 0,
-			down: 0,
-			left: 0,
-			right: 0,
-			forward: 0,
-			back: 0,
-			pitchUp: 0,
-			pitchDown: 0,
-			yawLeft: 0,
-			yawRight: 0,
-			rollLeft: 0,
-			rollRight: 0
-		};
-		this.moveVector = new THREE.Vector3( 0, 0, 0 );
-		this.rotationVector = new THREE.Vector3( 0, 0, 0 );
-
-		this.keydown = function ( event ) {
-
-			if ( event.altKey ) {
-
-				return;
-
-			} //event.preventDefault();
-
-
-			switch ( event.code ) {
-
-				case 'ShiftLeft':
-				case 'ShiftRight':
-					this.movementSpeedMultiplier = .1;
-					break;
-
-				case 'KeyW':
-					this.moveState.forward = 1;
-					break;
-
-				case 'KeyS':
-					this.moveState.back = 1;
-					break;
-
-				case 'KeyA':
-					this.moveState.left = 1;
-					break;
-
-				case 'KeyD':
-					this.moveState.right = 1;
-					break;
-
-				case 'KeyR':
-					this.moveState.up = 1;
-					break;
-
-				case 'KeyF':
-					this.moveState.down = 1;
-					break;
-
-				case 'ArrowUp':
-					this.moveState.pitchUp = 1;
-					break;
-
-				case 'ArrowDown':
-					this.moveState.pitchDown = 1;
-					break;
-
-				case 'ArrowLeft':
-					this.moveState.yawLeft = 1;
-					break;
-
-				case 'ArrowRight':
-					this.moveState.yawRight = 1;
-					break;
-
-				case 'KeyQ':
-					this.moveState.rollLeft = 1;
-					break;
-
-				case 'KeyE':
-					this.moveState.rollRight = 1;
-					break;
+			if ( domElement === undefined ) {
+
+				console.warn( 'THREE.FlyControls: The second parameter "domElement" is now mandatory.' );
+				domElement = document;
 
 			}
 
-			this.updateMovementVector();
-			this.updateRotationVector();
+			this.object = object;
+			this.domElement = domElement; // API
+
+			this.movementSpeed = 1.0;
+			this.rollSpeed = 0.005;
+			this.dragToLook = false;
+			this.autoForward = false; // disable default target object behavior
+			// internals
+
+			const scope = this;
+			const EPS = 0.000001;
+			const lastQuaternion = new THREE.Quaternion();
+			const lastPosition = new THREE.Vector3();
+			this.tmpQuaternion = new THREE.Quaternion();
+			this.mouseStatus = 0;
+			this.moveState = {
+				up: 0,
+				down: 0,
+				left: 0,
+				right: 0,
+				forward: 0,
+				back: 0,
+				pitchUp: 0,
+				pitchDown: 0,
+				yawLeft: 0,
+				yawRight: 0,
+				rollLeft: 0,
+				rollRight: 0
+			};
+			this.moveVector = new THREE.Vector3( 0, 0, 0 );
+			this.rotationVector = new THREE.Vector3( 0, 0, 0 );
 
-		};
+			this.keydown = function ( event ) {
 
-		this.keyup = function ( event ) {
+				if ( event.altKey ) {
 
-			switch ( event.code ) {
+					return;
 
-				case 'ShiftLeft':
-				case 'ShiftRight':
-					this.movementSpeedMultiplier = 1;
-					break;
+				} //event.preventDefault();
 
-				case 'KeyW':
-					this.moveState.forward = 0;
-					break;
 
-				case 'KeyS':
-					this.moveState.back = 0;
-					break;
+				switch ( event.code ) {
 
-				case 'KeyA':
-					this.moveState.left = 0;
-					break;
+					case 'ShiftLeft':
+					case 'ShiftRight':
+						this.movementSpeedMultiplier = .1;
+						break;
 
-				case 'KeyD':
-					this.moveState.right = 0;
-					break;
+					case 'KeyW':
+						this.moveState.forward = 1;
+						break;
 
-				case 'KeyR':
-					this.moveState.up = 0;
-					break;
+					case 'KeyS':
+						this.moveState.back = 1;
+						break;
 
-				case 'KeyF':
-					this.moveState.down = 0;
-					break;
+					case 'KeyA':
+						this.moveState.left = 1;
+						break;
 
-				case 'ArrowUp':
-					this.moveState.pitchUp = 0;
-					break;
+					case 'KeyD':
+						this.moveState.right = 1;
+						break;
 
-				case 'ArrowDown':
-					this.moveState.pitchDown = 0;
-					break;
+					case 'KeyR':
+						this.moveState.up = 1;
+						break;
 
-				case 'ArrowLeft':
-					this.moveState.yawLeft = 0;
-					break;
+					case 'KeyF':
+						this.moveState.down = 1;
+						break;
 
-				case 'ArrowRight':
-					this.moveState.yawRight = 0;
-					break;
+					case 'ArrowUp':
+						this.moveState.pitchUp = 1;
+						break;
 
-				case 'KeyQ':
-					this.moveState.rollLeft = 0;
-					break;
+					case 'ArrowDown':
+						this.moveState.pitchDown = 1;
+						break;
 
-				case 'KeyE':
-					this.moveState.rollRight = 0;
-					break;
+					case 'ArrowLeft':
+						this.moveState.yawLeft = 1;
+						break;
 
-			}
+					case 'ArrowRight':
+						this.moveState.yawRight = 1;
+						break;
 
-			this.updateMovementVector();
-			this.updateRotationVector();
+					case 'KeyQ':
+						this.moveState.rollLeft = 1;
+						break;
+
+					case 'KeyE':
+						this.moveState.rollRight = 1;
+						break;
 
-		};
+				}
 
-		this.mousedown = function ( event ) {
+				this.updateMovementVector();
+				this.updateRotationVector();
 
-			if ( this.domElement !== document ) {
+			};
 
-				this.domElement.focus();
+			this.keyup = function ( event ) {
 
-			}
+				switch ( event.code ) {
 
-			event.preventDefault();
+					case 'ShiftLeft':
+					case 'ShiftRight':
+						this.movementSpeedMultiplier = 1;
+						break;
 
-			if ( this.dragToLook ) {
+					case 'KeyW':
+						this.moveState.forward = 0;
+						break;
 
-				this.mouseStatus ++;
+					case 'KeyS':
+						this.moveState.back = 0;
+						break;
 
-			} else {
+					case 'KeyA':
+						this.moveState.left = 0;
+						break;
 
-				switch ( event.button ) {
+					case 'KeyD':
+						this.moveState.right = 0;
+						break;
 
-					case 0:
-						this.moveState.forward = 1;
+					case 'KeyR':
+						this.moveState.up = 0;
 						break;
 
-					case 2:
-						this.moveState.back = 1;
+					case 'KeyF':
+						this.moveState.down = 0;
+						break;
+
+					case 'ArrowUp':
+						this.moveState.pitchUp = 0;
+						break;
+
+					case 'ArrowDown':
+						this.moveState.pitchDown = 0;
+						break;
+
+					case 'ArrowLeft':
+						this.moveState.yawLeft = 0;
+						break;
+
+					case 'ArrowRight':
+						this.moveState.yawRight = 0;
+						break;
+
+					case 'KeyQ':
+						this.moveState.rollLeft = 0;
+						break;
+
+					case 'KeyE':
+						this.moveState.rollRight = 0;
 						break;
 
 				}
 
 				this.updateMovementVector();
+				this.updateRotationVector();
 
-			}
+			};
 
-		};
+			this.mousedown = function ( event ) {
 
-		this.mousemove = function ( event ) {
+				if ( this.domElement !== document ) {
 
-			if ( ! this.dragToLook || this.mouseStatus > 0 ) {
+					this.domElement.focus();
 
-				var container = this.getContainerDimensions();
-				var halfWidth = container.size[ 0 ] / 2;
-				var halfHeight = container.size[ 1 ] / 2;
-				this.moveState.yawLeft = - ( event.pageX - container.offset[ 0 ] - halfWidth ) / halfWidth;
-				this.moveState.pitchDown = ( event.pageY - container.offset[ 1 ] - halfHeight ) / halfHeight;
-				this.updateRotationVector();
+				}
 
-			}
+				event.preventDefault();
 
-		};
+				if ( this.dragToLook ) {
 
-		this.mouseup = function ( event ) {
+					this.mouseStatus ++;
 
-			event.preventDefault();
+				} else {
 
-			if ( this.dragToLook ) {
+					switch ( event.button ) {
 
-				this.mouseStatus --;
-				this.moveState.yawLeft = this.moveState.pitchDown = 0;
+						case 0:
+							this.moveState.forward = 1;
+							break;
 
-			} else {
+						case 2:
+							this.moveState.back = 1;
+							break;
 
-				switch ( event.button ) {
+					}
 
-					case 0:
-						this.moveState.forward = 0;
-						break;
+					this.updateMovementVector();
 
-					case 2:
-						this.moveState.back = 0;
-						break;
+				}
+
+			};
+
+			this.mousemove = function ( event ) {
+
+				if ( ! this.dragToLook || this.mouseStatus > 0 ) {
+
+					const container = this.getContainerDimensions();
+					const halfWidth = container.size[ 0 ] / 2;
+					const halfHeight = container.size[ 1 ] / 2;
+					this.moveState.yawLeft = - ( event.pageX - container.offset[ 0 ] - halfWidth ) / halfWidth;
+					this.moveState.pitchDown = ( event.pageY - container.offset[ 1 ] - halfHeight ) / halfHeight;
+					this.updateRotationVector();
 
 				}
 
-				this.updateMovementVector();
+			};
 
-			}
+			this.mouseup = function ( event ) {
 
-			this.updateRotationVector();
+				event.preventDefault();
+
+				if ( this.dragToLook ) {
+
+					this.mouseStatus --;
+					this.moveState.yawLeft = this.moveState.pitchDown = 0;
+
+				} else {
+
+					switch ( event.button ) {
 
-		};
+						case 0:
+							this.moveState.forward = 0;
+							break;
 
-		this.update = function () {
+						case 2:
+							this.moveState.back = 0;
+							break;
 
-			var lastQuaternion = new THREE.Quaternion();
-			var lastPosition = new THREE.Vector3();
-			return function ( delta ) {
+					}
 
-				var moveMult = delta * scope.movementSpeed;
-				var rotMult = delta * scope.rollSpeed;
+					this.updateMovementVector();
+
+				}
+
+				this.updateRotationVector();
+
+			};
+
+			this.update = function ( delta ) {
+
+				const moveMult = delta * scope.movementSpeed;
+				const rotMult = delta * scope.rollSpeed;
 				scope.object.translateX( scope.moveVector.x * moveMult );
 				scope.object.translateY( scope.moveVector.y * moveMult );
 				scope.object.translateZ( scope.moveVector.z * moveMult );
@@ -274,7 +276,7 @@
 
 				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
 
-					scope.dispatchEvent( changeEvent );
+					scope.dispatchEvent( _changeEvent );
 					lastQuaternion.copy( scope.object.quaternion );
 					lastPosition.copy( scope.object.position );
 
@@ -282,95 +284,82 @@
 
 			};
 
-		}();
-
-		this.updateMovementVector = function () {
-
-			var forward = this.moveState.forward || this.autoForward && ! this.moveState.back ? 1 : 0;
-			this.moveVector.x = - this.moveState.left + this.moveState.right;
-			this.moveVector.y = - this.moveState.down + this.moveState.up;
-			this.moveVector.z = - forward + this.moveState.back; //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
-
-		};
-
-		this.updateRotationVector = function () {
-
-			this.rotationVector.x = - this.moveState.pitchDown + this.moveState.pitchUp;
-			this.rotationVector.y = - this.moveState.yawRight + this.moveState.yawLeft;
-			this.rotationVector.z = - this.moveState.rollRight + this.moveState.rollLeft; //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
+			this.updateMovementVector = function () {
 
-		};
+				const forward = this.moveState.forward || this.autoForward && ! this.moveState.back ? 1 : 0;
+				this.moveVector.x = - this.moveState.left + this.moveState.right;
+				this.moveVector.y = - this.moveState.down + this.moveState.up;
+				this.moveVector.z = - forward + this.moveState.back; //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
 
-		this.getContainerDimensions = function () {
+			};
 
-			if ( this.domElement != document ) {
+			this.updateRotationVector = function () {
 
-				return {
-					size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
-					offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
-				};
+				this.rotationVector.x = - this.moveState.pitchDown + this.moveState.pitchUp;
+				this.rotationVector.y = - this.moveState.yawRight + this.moveState.yawLeft;
+				this.rotationVector.z = - this.moveState.rollRight + this.moveState.rollLeft; //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
 
-			} else {
+			};
 
-				return {
-					size: [ window.innerWidth, window.innerHeight ],
-					offset: [ 0, 0 ]
-				};
+			this.getContainerDimensions = function () {
 
-			}
+				if ( this.domElement != document ) {
 
-		};
+					return {
+						size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
+						offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
+					};
 
-		function bind( scope, fn ) {
+				} else {
 
-			return function () {
+					return {
+						size: [ window.innerWidth, window.innerHeight ],
+						offset: [ 0, 0 ]
+					};
 
-				fn.apply( scope, arguments );
+				}
 
 			};
 
-		}
-
-		function contextmenu( event ) {
+			this.dispose = function () {
 
-			event.preventDefault();
+				this.domElement.removeEventListener( 'contextmenu', contextmenu );
+				this.domElement.removeEventListener( 'mousedown', _mousedown );
+				this.domElement.removeEventListener( 'mousemove', _mousemove );
+				this.domElement.removeEventListener( 'mouseup', _mouseup );
+				window.removeEventListener( 'keydown', _keydown );
+				window.removeEventListener( 'keyup', _keyup );
 
-		}
+			};
 
-		this.dispose = function () {
+			const _mousemove = this.mousemove.bind( this );
 
-			this.domElement.removeEventListener( 'contextmenu', contextmenu );
-			this.domElement.removeEventListener( 'mousedown', _mousedown );
-			this.domElement.removeEventListener( 'mousemove', _mousemove );
-			this.domElement.removeEventListener( 'mouseup', _mouseup );
-			window.removeEventListener( 'keydown', _keydown );
-			window.removeEventListener( 'keyup', _keyup );
+			const _mousedown = this.mousedown.bind( this );
 
-		};
+			const _mouseup = this.mouseup.bind( this );
 
-		var _mousemove = bind( this, this.mousemove );
+			const _keydown = this.keydown.bind( this );
 
-		var _mousedown = bind( this, this.mousedown );
+			const _keyup = this.keyup.bind( this );
 
-		var _mouseup = bind( this, this.mouseup );
+			this.domElement.addEventListener( 'contextmenu', contextmenu );
+			this.domElement.addEventListener( 'mousemove', _mousemove );
+			this.domElement.addEventListener( 'mousedown', _mousedown );
+			this.domElement.addEventListener( 'mouseup', _mouseup );
+			window.addEventListener( 'keydown', _keydown );
+			window.addEventListener( 'keyup', _keyup );
+			this.updateMovementVector();
+			this.updateRotationVector();
 
-		var _keydown = bind( this, this.keydown );
+		}
 
-		var _keyup = bind( this, this.keyup );
+	}
 
-		this.domElement.addEventListener( 'contextmenu', contextmenu );
-		this.domElement.addEventListener( 'mousemove', _mousemove );
-		this.domElement.addEventListener( 'mousedown', _mousedown );
-		this.domElement.addEventListener( 'mouseup', _mouseup );
-		window.addEventListener( 'keydown', _keydown );
-		window.addEventListener( 'keyup', _keyup );
-		this.updateMovementVector();
-		this.updateRotationVector();
+	function contextmenu( event ) {
 
-	};
+		event.preventDefault();
 
-	FlyControls.prototype = Object.create( THREE.EventDispatcher.prototype );
-	FlyControls.prototype.constructor = FlyControls;
+	}
 
 	THREE.FlyControls = FlyControls;
 

File diff ditekan karena terlalu besar
+ 645 - 642
examples/js/controls/OrbitControls.js


+ 105 - 96
examples/js/controls/PointerLockControls.js

@@ -1,150 +1,159 @@
 ( function () {
 
-	var PointerLockControls = function ( camera, domElement ) {
+	const _euler = new THREE.Euler( 0, 0, 0, 'YXZ' );
 
-		if ( domElement === undefined ) {
+	const _vector = new THREE.Vector3();
 
-			console.warn( 'THREE.PointerLockControls: The second parameter "domElement" is now mandatory.' );
-			domElement = document.body;
+	const _changeEvent = {
+		type: 'change'
+	};
+	const _lockEvent = {
+		type: 'lock'
+	};
+	const _unlockEvent = {
+		type: 'unlock'
+	};
 
-		}
+	const _PI_2 = Math.PI / 2;
 
-		this.domElement = domElement;
-		this.isLocked = false; // Set to constrain the pitch of the camera
-		// Range is 0 to Math.PI radians
-
-		this.minPolarAngle = 0; // radians
-
-		this.maxPolarAngle = Math.PI; // radians
-		//
-		// internals
-		//
-
-		var scope = this;
-		var changeEvent = {
-			type: 'change'
-		};
-		var lockEvent = {
-			type: 'lock'
-		};
-		var unlockEvent = {
-			type: 'unlock'
-		};
-		var euler = new THREE.Euler( 0, 0, 0, 'YXZ' );
-		var PI_2 = Math.PI / 2;
-		var vec = new THREE.Vector3();
-
-		function onMouseMove( event ) {
-
-			if ( scope.isLocked === false ) return;
-			var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
-			var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
-			euler.setFromQuaternion( camera.quaternion );
-			euler.y -= movementX * 0.002;
-			euler.x -= movementY * 0.002;
-			euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );
-			camera.quaternion.setFromEuler( euler );
-			scope.dispatchEvent( changeEvent );
+	class PointerLockControls extends THREE.EventDispatcher {
 
-		}
+		constructor( camera, domElement ) {
+
+			super();
+
+			if ( domElement === undefined ) {
+
+				console.warn( 'THREE.PointerLockControls: The second parameter "domElement" is now mandatory.' );
+				domElement = document.body;
+
+			}
+
+			this.domElement = domElement;
+			this.isLocked = false; // Set to constrain the pitch of the camera
+			// Range is 0 to Math.PI radians
+
+			this.minPolarAngle = 0; // radians
 
-		function onPointerlockChange() {
+			this.maxPolarAngle = Math.PI; // radians
 
-			if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
+			const scope = this;
 
-				scope.dispatchEvent( lockEvent );
-				scope.isLocked = true;
+			function onMouseMove( event ) {
 
-			} else {
+				if ( scope.isLocked === false ) return;
+				const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
+				const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
 
-				scope.dispatchEvent( unlockEvent );
-				scope.isLocked = false;
+				_euler.setFromQuaternion( camera.quaternion );
+
+				_euler.y -= movementX * 0.002;
+				_euler.x -= movementY * 0.002;
+				_euler.x = Math.max( _PI_2 - scope.maxPolarAngle, Math.min( _PI_2 - scope.minPolarAngle, _euler.x ) );
+				camera.quaternion.setFromEuler( _euler );
+				scope.dispatchEvent( _changeEvent );
 
 			}
 
-		}
+			function onPointerlockChange() {
 
-		function onPointerlockError() {
+				if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
 
-			console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
+					scope.dispatchEvent( _lockEvent );
+					scope.isLocked = true;
 
-		}
+				} else {
 
-		this.connect = function () {
+					scope.dispatchEvent( _unlockEvent );
+					scope.isLocked = false;
 
-			scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
-			scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange );
-			scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError );
+				}
 
-		};
+			}
 
-		this.disconnect = function () {
+			function onPointerlockError() {
 
-			scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
-			scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange );
-			scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError );
+				console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
 
-		};
+			}
 
-		this.dispose = function () {
+			this.connect = function () {
 
-			this.disconnect();
+				scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
+				scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange );
+				scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError );
 
-		};
+			};
 
-		this.getObject = function () {
+			this.disconnect = function () {
 
-			// retaining this method for backward compatibility
-			return camera;
+				scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
+				scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange );
+				scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError );
 
-		};
+			};
+
+			this.dispose = function () {
 
-		this.getDirection = function () {
+				this.disconnect();
 
-			var direction = new THREE.Vector3( 0, 0, - 1 );
-			return function ( v ) {
+			};
 
-				return v.copy( direction ).applyQuaternion( camera.quaternion );
+			this.getObject = function () {
+
+				// retaining this method for backward compatibility
+				return camera;
 
 			};
 
-		}();
+			this.getDirection = function () {
 
-		this.moveForward = function ( distance ) {
+				const direction = new THREE.Vector3( 0, 0, - 1 );
+				return function ( v ) {
 
-			// move forward parallel to the xz-plane
-			// assumes camera.up is y-up
-			vec.setFromMatrixColumn( camera.matrix, 0 );
-			vec.crossVectors( camera.up, vec );
-			camera.position.addScaledVector( vec, distance );
+					return v.copy( direction ).applyQuaternion( camera.quaternion );
 
-		};
+				};
 
-		this.moveRight = function ( distance ) {
+			}();
 
-			vec.setFromMatrixColumn( camera.matrix, 0 );
-			camera.position.addScaledVector( vec, distance );
+			this.moveForward = function ( distance ) {
 
-		};
+				// move forward parallel to the xz-plane
+				// assumes camera.up is y-up
+				_vector.setFromMatrixColumn( camera.matrix, 0 );
 
-		this.lock = function () {
+				_vector.crossVectors( camera.up, _vector );
 
-			this.domElement.requestPointerLock();
+				camera.position.addScaledVector( _vector, distance );
 
-		};
+			};
 
-		this.unlock = function () {
+			this.moveRight = function ( distance ) {
 
-			scope.domElement.ownerDocument.exitPointerLock();
+				_vector.setFromMatrixColumn( camera.matrix, 0 );
 
-		};
+				camera.position.addScaledVector( _vector, distance );
 
-		this.connect();
+			};
 
-	};
+			this.lock = function () {
+
+				this.domElement.requestPointerLock();
+
+			};
+
+			this.unlock = function () {
+
+				scope.domElement.ownerDocument.exitPointerLock();
+
+			};
+
+			this.connect();
+
+		}
 
-	PointerLockControls.prototype = Object.create( THREE.EventDispatcher.prototype );
-	PointerLockControls.prototype.constructor = PointerLockControls;
+	}
 
 	THREE.PointerLockControls = PointerLockControls;
 

+ 457 - 456
examples/js/controls/TrackballControls.js

@@ -1,208 +1,189 @@
 ( function () {
 
-	var TrackballControls = function ( object, domElement ) {
-
-		if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
-		if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
-		var scope = this;
-		var STATE = {
-			NONE: - 1,
-			ROTATE: 0,
-			ZOOM: 1,
-			PAN: 2,
-			TOUCH_ROTATE: 3,
-			TOUCH_ZOOM_PAN: 4
-		};
-		this.object = object;
-		this.domElement = domElement; // API
-
-		this.enabled = true;
-		this.screen = {
-			left: 0,
-			top: 0,
-			width: 0,
-			height: 0
-		};
-		this.rotateSpeed = 1.0;
-		this.zoomSpeed = 1.2;
-		this.panSpeed = 0.3;
-		this.noRotate = false;
-		this.noZoom = false;
-		this.noPan = false;
-		this.staticMoving = false;
-		this.dynamicDampingFactor = 0.2;
-		this.minDistance = 0;
-		this.maxDistance = Infinity;
-		this.keys = [ 'KeyA',
-			/*A*/
-	 'KeyS',
-			/*S*/
-	 'KeyD'
-			/*D*/
-		];
-		this.mouseButtons = {
-			LEFT: THREE.MOUSE.ROTATE,
-			MIDDLE: THREE.MOUSE.DOLLY,
-			RIGHT: THREE.MOUSE.PAN
-		}; // internals
-
-		this.target = new THREE.Vector3();
-		var EPS = 0.000001;
-		var lastPosition = new THREE.Vector3();
-		var lastZoom = 1;
-
-		var _state = STATE.NONE,
-			_keyState = STATE.NONE,
-			_eye = new THREE.Vector3(),
-			_movePrev = new THREE.Vector2(),
-			_moveCurr = new THREE.Vector2(),
-			_lastAxis = new THREE.Vector3(),
-			_lastAngle = 0,
-			_zoomStart = new THREE.Vector2(),
-			_zoomEnd = new THREE.Vector2(),
-			_touchZoomDistanceStart = 0,
-			_touchZoomDistanceEnd = 0,
-			_panStart = new THREE.Vector2(),
-			_panEnd = new THREE.Vector2(); // for reset
-
-
-		this.target0 = this.target.clone();
-		this.position0 = this.object.position.clone();
-		this.up0 = this.object.up.clone();
-		this.zoom0 = this.object.zoom; // events
-
-		var changeEvent = {
-			type: 'change'
-		};
-		var startEvent = {
-			type: 'start'
-		};
-		var endEvent = {
-			type: 'end'
-		}; // methods
-
-		this.handleResize = function () {
-
-			var box = scope.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function
-
-			var d = scope.domElement.ownerDocument.documentElement;
-			scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
-			scope.screen.top = box.top + window.pageYOffset - d.clientTop;
-			scope.screen.width = box.width;
-			scope.screen.height = box.height;
-
-		};
-
-		var getMouseOnScreen = function () {
-
-			var vector = new THREE.Vector2();
-			return function getMouseOnScreen( pageX, pageY ) {
-
-				vector.set( ( pageX - scope.screen.left ) / scope.screen.width, ( pageY - scope.screen.top ) / scope.screen.height );
-				return vector;
+	const _changeEvent = {
+		type: 'change'
+	};
+	const _startEvent = {
+		type: 'start'
+	};
+	const _endEvent = {
+		type: 'end'
+	};
 
+	class TrackballControls extends THREE.EventDispatcher {
+
+		constructor( object, domElement ) {
+
+			super();
+			if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
+			if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
+			const scope = this;
+			const STATE = {
+				NONE: - 1,
+				ROTATE: 0,
+				ZOOM: 1,
+				PAN: 2,
+				TOUCH_ROTATE: 3,
+				TOUCH_ZOOM_PAN: 4
 			};
-
-		}();
-
-		var getMouseOnCircle = function () {
-
-			var vector = new THREE.Vector2();
-			return function getMouseOnCircle( pageX, pageY ) {
-
-				vector.set( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ), ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width // screen.width intentional
-				);
-				return vector;
+			this.object = object;
+			this.domElement = domElement; // API
+
+			this.enabled = true;
+			this.screen = {
+				left: 0,
+				top: 0,
+				width: 0,
+				height: 0
+			};
+			this.rotateSpeed = 1.0;
+			this.zoomSpeed = 1.2;
+			this.panSpeed = 0.3;
+			this.noRotate = false;
+			this.noZoom = false;
+			this.noPan = false;
+			this.staticMoving = false;
+			this.dynamicDampingFactor = 0.2;
+			this.minDistance = 0;
+			this.maxDistance = Infinity;
+			this.keys = [ 'KeyA',
+				/*A*/
+		 'KeyS',
+				/*S*/
+		 'KeyD'
+				/*D*/
+			];
+			this.mouseButtons = {
+				LEFT: THREE.MOUSE.ROTATE,
+				MIDDLE: THREE.MOUSE.DOLLY,
+				RIGHT: THREE.MOUSE.PAN
+			}; // internals
+
+			this.target = new THREE.Vector3();
+			const EPS = 0.000001;
+			const lastPosition = new THREE.Vector3();
+			let lastZoom = 1;
+			let _state = STATE.NONE,
+				_keyState = STATE.NONE,
+				_touchZoomDistanceStart = 0,
+				_touchZoomDistanceEnd = 0,
+				_lastAngle = 0;
+
+			const _eye = new THREE.Vector3(),
+				_movePrev = new THREE.Vector2(),
+				_moveCurr = new THREE.Vector2(),
+				_lastAxis = new THREE.Vector3(),
+				_zoomStart = new THREE.Vector2(),
+				_zoomEnd = new THREE.Vector2(),
+				_panStart = new THREE.Vector2(),
+				_panEnd = new THREE.Vector2(); // for reset
+
+
+			this.target0 = this.target.clone();
+			this.position0 = this.object.position.clone();
+			this.up0 = this.object.up.clone();
+			this.zoom0 = this.object.zoom; // methods
+
+			this.handleResize = function () {
+
+				const box = scope.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function
+
+				const d = scope.domElement.ownerDocument.documentElement;
+				scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
+				scope.screen.top = box.top + window.pageYOffset - d.clientTop;
+				scope.screen.width = box.width;
+				scope.screen.height = box.height;
 
 			};
 
-		}();
+			const getMouseOnScreen = function () {
 
-		this.rotateCamera = function () {
+				const vector = new THREE.Vector2();
+				return function getMouseOnScreen( pageX, pageY ) {
 
-			var axis = new THREE.Vector3(),
-				quaternion = new THREE.Quaternion(),
-				eyeDirection = new THREE.Vector3(),
-				objectUpDirection = new THREE.Vector3(),
-				objectSidewaysDirection = new THREE.Vector3(),
-				moveDirection = new THREE.Vector3(),
-				angle;
-			return function rotateCamera() {
+					vector.set( ( pageX - scope.screen.left ) / scope.screen.width, ( pageY - scope.screen.top ) / scope.screen.height );
+					return vector;
 
-				moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
-				angle = moveDirection.length();
+				};
 
-				if ( angle ) {
+			}();
 
-					_eye.copy( scope.object.position ).sub( scope.target );
+			const getMouseOnCircle = function () {
 
-					eyeDirection.copy( _eye ).normalize();
-					objectUpDirection.copy( scope.object.up ).normalize();
-					objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
-					objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
-					objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
-					moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
-					axis.crossVectors( moveDirection, _eye ).normalize();
-					angle *= scope.rotateSpeed;
-					quaternion.setFromAxisAngle( axis, angle );
+				const vector = new THREE.Vector2();
+				return function getMouseOnCircle( pageX, pageY ) {
 
-					_eye.applyQuaternion( quaternion );
+					vector.set( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ), ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width // screen.width intentional
+					);
+					return vector;
 
-					scope.object.up.applyQuaternion( quaternion );
+				};
 
-					_lastAxis.copy( axis );
+			}();
 
-					_lastAngle = angle;
+			this.rotateCamera = function () {
 
-				} else if ( ! scope.staticMoving && _lastAngle ) {
+				const axis = new THREE.Vector3(),
+					quaternion = new THREE.Quaternion(),
+					eyeDirection = new THREE.Vector3(),
+					objectUpDirection = new THREE.Vector3(),
+					objectSidewaysDirection = new THREE.Vector3(),
+					moveDirection = new THREE.Vector3();
+				return function rotateCamera() {
 
-					_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
+					moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
+					let angle = moveDirection.length();
 
-					_eye.copy( scope.object.position ).sub( scope.target );
+					if ( angle ) {
 
-					quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
+						_eye.copy( scope.object.position ).sub( scope.target );
 
-					_eye.applyQuaternion( quaternion );
+						eyeDirection.copy( _eye ).normalize();
+						objectUpDirection.copy( scope.object.up ).normalize();
+						objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
+						objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
+						objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
+						moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
+						axis.crossVectors( moveDirection, _eye ).normalize();
+						angle *= scope.rotateSpeed;
+						quaternion.setFromAxisAngle( axis, angle );
 
-					scope.object.up.applyQuaternion( quaternion );
+						_eye.applyQuaternion( quaternion );
 
-				}
+						scope.object.up.applyQuaternion( quaternion );
 
-				_movePrev.copy( _moveCurr );
+						_lastAxis.copy( axis );
 
-			};
-
-		}();
+						_lastAngle = angle;
 
-		this.zoomCamera = function () {
+					} else if ( ! scope.staticMoving && _lastAngle ) {
 
-			var factor;
+						_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
 
-			if ( _state === STATE.TOUCH_ZOOM_PAN ) {
+						_eye.copy( scope.object.position ).sub( scope.target );
 
-				factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
-				_touchZoomDistanceStart = _touchZoomDistanceEnd;
+						quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
 
-				if ( scope.object.isPerspectiveCamera ) {
+						_eye.applyQuaternion( quaternion );
 
-					_eye.multiplyScalar( factor );
+						scope.object.up.applyQuaternion( quaternion );
 
-				} else if ( scope.object.isOrthographicCamera ) {
+					}
 
-					scope.object.zoom *= factor;
-					scope.object.updateProjectionMatrix();
+					_movePrev.copy( _moveCurr );
 
-				} else {
+				};
 
-					console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+			}();
 
-				}
+			this.zoomCamera = function () {
 
-			} else {
+				let factor;
 
-				factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
+				if ( _state === STATE.TOUCH_ZOOM_PAN ) {
 
-				if ( factor !== 1.0 && factor > 0.0 ) {
+					factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+					_touchZoomDistanceStart = _touchZoomDistanceEnd;
 
 					if ( scope.object.isPerspectiveCamera ) {
 
@@ -210,7 +191,7 @@
 
 					} else if ( scope.object.isOrthographicCamera ) {
 
-						scope.object.zoom /= factor;
+						scope.object.zoom *= factor;
 						scope.object.updateProjectionMatrix();
 
 					} else {
@@ -219,55 +200,36 @@
 
 					}
 
-				}
-
-				if ( scope.staticMoving ) {
-
-					_zoomStart.copy( _zoomEnd );
-
 				} else {
 
-					_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
+					factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
 
-				}
-
-			}
+					if ( factor !== 1.0 && factor > 0.0 ) {
 
-		};
+						if ( scope.object.isPerspectiveCamera ) {
 
-		this.panCamera = function () {
+							_eye.multiplyScalar( factor );
 
-			var mouseChange = new THREE.Vector2(),
-				objectUp = new THREE.Vector3(),
-				pan = new THREE.Vector3();
-			return function panCamera() {
+						} else if ( scope.object.isOrthographicCamera ) {
 
-				mouseChange.copy( _panEnd ).sub( _panStart );
+							scope.object.zoom /= factor;
+							scope.object.updateProjectionMatrix();
 
-				if ( mouseChange.lengthSq() ) {
+						} else {
 
-					if ( scope.object.isOrthographicCamera ) {
+							console.warn( 'THREE.TrackballControls: Unsupported camera type' );
 
-						var scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
-						var scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
-						mouseChange.x *= scale_x;
-						mouseChange.y *= scale_y;
+						}
 
 					}
 
-					mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
-					pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
-					pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
-					scope.object.position.add( pan );
-					scope.target.add( pan );
-
 					if ( scope.staticMoving ) {
 
-						_panStart.copy( _panEnd );
+						_zoomStart.copy( _zoomEnd );
 
 					} else {
 
-						_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
+						_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
 
 					}
 
@@ -275,441 +237,480 @@
 
 			};
 
-		}();
+			this.panCamera = function () {
 
-		this.checkDistances = function () {
+				const mouseChange = new THREE.Vector2(),
+					objectUp = new THREE.Vector3(),
+					pan = new THREE.Vector3();
+				return function panCamera() {
 
-			if ( ! scope.noZoom || ! scope.noPan ) {
+					mouseChange.copy( _panEnd ).sub( _panStart );
 
-				if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
+					if ( mouseChange.lengthSq() ) {
 
-					scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
+						if ( scope.object.isOrthographicCamera ) {
 
-					_zoomStart.copy( _zoomEnd );
+							const scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
+							const scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
+							mouseChange.x *= scale_x;
+							mouseChange.y *= scale_y;
 
-				}
+						}
 
-				if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
+						mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
+						pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
+						pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
+						scope.object.position.add( pan );
+						scope.target.add( pan );
 
-					scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
+						if ( scope.staticMoving ) {
 
-					_zoomStart.copy( _zoomEnd );
+							_panStart.copy( _panEnd );
 
-				}
+						} else {
 
-			}
+							_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
 
-		};
+						}
 
-		this.update = function () {
+					}
 
-			_eye.subVectors( scope.object.position, scope.target );
+				};
 
-			if ( ! scope.noRotate ) {
+			}();
 
-				scope.rotateCamera();
+			this.checkDistances = function () {
 
-			}
+				if ( ! scope.noZoom || ! scope.noPan ) {
 
-			if ( ! scope.noZoom ) {
+					if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
 
-				scope.zoomCamera();
+						scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
 
-			}
+						_zoomStart.copy( _zoomEnd );
 
-			if ( ! scope.noPan ) {
+					}
 
-				scope.panCamera();
+					if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
 
-			}
+						scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
 
-			scope.object.position.addVectors( scope.target, _eye );
+						_zoomStart.copy( _zoomEnd );
 
-			if ( scope.object.isPerspectiveCamera ) {
+					}
 
-				scope.checkDistances();
-				scope.object.lookAt( scope.target );
+				}
+
+			};
 
-				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
+			this.update = function () {
 
-					scope.dispatchEvent( changeEvent );
-					lastPosition.copy( scope.object.position );
+				_eye.subVectors( scope.object.position, scope.target );
+
+				if ( ! scope.noRotate ) {
+
+					scope.rotateCamera();
 
 				}
 
-			} else if ( scope.object.isOrthographicCamera ) {
+				if ( ! scope.noZoom ) {
 
-				scope.object.lookAt( scope.target );
+					scope.zoomCamera();
 
-				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
+				}
 
-					scope.dispatchEvent( changeEvent );
-					lastPosition.copy( scope.object.position );
-					lastZoom = scope.object.zoom;
+				if ( ! scope.noPan ) {
+
+					scope.panCamera();
 
 				}
 
-			} else {
+				scope.object.position.addVectors( scope.target, _eye );
 
-				console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+				if ( scope.object.isPerspectiveCamera ) {
 
-			}
+					scope.checkDistances();
+					scope.object.lookAt( scope.target );
 
-		};
+					if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
 
-		this.reset = function () {
+						scope.dispatchEvent( _changeEvent );
+						lastPosition.copy( scope.object.position );
 
-			_state = STATE.NONE;
-			_keyState = STATE.NONE;
-			scope.target.copy( scope.target0 );
-			scope.object.position.copy( scope.position0 );
-			scope.object.up.copy( scope.up0 );
-			scope.object.zoom = scope.zoom0;
-			scope.object.updateProjectionMatrix();
+					}
 
-			_eye.subVectors( scope.object.position, scope.target );
+				} else if ( scope.object.isOrthographicCamera ) {
 
-			scope.object.lookAt( scope.target );
-			scope.dispatchEvent( changeEvent );
-			lastPosition.copy( scope.object.position );
-			lastZoom = scope.object.zoom;
+					scope.object.lookAt( scope.target );
 
-		}; // listeners
+					if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
 
+						scope.dispatchEvent( _changeEvent );
+						lastPosition.copy( scope.object.position );
+						lastZoom = scope.object.zoom;
 
-		function onPointerDown( event ) {
+					}
 
-			if ( scope.enabled === false ) return;
+				} else {
 
-			switch ( event.pointerType ) {
+					console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+
+				}
+
+			};
+
+			this.reset = function () {
+
+				_state = STATE.NONE;
+				_keyState = STATE.NONE;
+				scope.target.copy( scope.target0 );
+				scope.object.position.copy( scope.position0 );
+				scope.object.up.copy( scope.up0 );
+				scope.object.zoom = scope.zoom0;
+				scope.object.updateProjectionMatrix();
+
+				_eye.subVectors( scope.object.position, scope.target );
+
+				scope.object.lookAt( scope.target );
+				scope.dispatchEvent( _changeEvent );
+				lastPosition.copy( scope.object.position );
+				lastZoom = scope.object.zoom;
+
+			}; // listeners
+
+
+			function onPointerDown( event ) {
+
+				if ( scope.enabled === false ) return;
+
+				switch ( event.pointerType ) {
+
+					case 'mouse':
+					case 'pen':
+						onMouseDown( event );
+						break;
+				// TODO touch
 
-				case 'mouse':
-				case 'pen':
-					onMouseDown( event );
-					break;
-			// TODO touch
+				}
 
 			}
 
-		}
+			function onPointerMove( event ) {
 
-		function onPointerMove( event ) {
+				if ( scope.enabled === false ) return;
 
-			if ( scope.enabled === false ) return;
+				switch ( event.pointerType ) {
 
-			switch ( event.pointerType ) {
+					case 'mouse':
+					case 'pen':
+						onMouseMove( event );
+						break;
+				// TODO touch
 
-				case 'mouse':
-				case 'pen':
-					onMouseMove( event );
-					break;
-			// TODO touch
+				}
 
 			}
 
-		}
+			function onPointerUp( event ) {
 
-		function onPointerUp( event ) {
+				if ( scope.enabled === false ) return;
 
-			if ( scope.enabled === false ) return;
+				switch ( event.pointerType ) {
 
-			switch ( event.pointerType ) {
+					case 'mouse':
+					case 'pen':
+						onMouseUp( event );
+						break;
+				// TODO touch
 
-				case 'mouse':
-				case 'pen':
-					onMouseUp( event );
-					break;
-			// TODO touch
+				}
 
 			}
 
-		}
+			function keydown( event ) {
 
-		function keydown( event ) {
+				if ( scope.enabled === false ) return;
+				window.removeEventListener( 'keydown', keydown );
 
-			if ( scope.enabled === false ) return;
-			window.removeEventListener( 'keydown', keydown );
+				if ( _keyState !== STATE.NONE ) {
 
-			if ( _keyState !== STATE.NONE ) {
+					return;
 
-				return;
+				} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
 
-			} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
+					_keyState = STATE.ROTATE;
 
-				_keyState = STATE.ROTATE;
+				} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
 
-			} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
+					_keyState = STATE.ZOOM;
 
-				_keyState = STATE.ZOOM;
+				} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
 
-			} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
+					_keyState = STATE.PAN;
 
-				_keyState = STATE.PAN;
+				}
 
 			}
 
-		}
+			function keyup() {
 
-		function keyup() {
+				if ( scope.enabled === false ) return;
+				_keyState = STATE.NONE;
+				window.addEventListener( 'keydown', keydown );
 
-			if ( scope.enabled === false ) return;
-			_keyState = STATE.NONE;
-			window.addEventListener( 'keydown', keydown );
+			}
 
-		}
+			function onMouseDown( event ) {
 
-		function onMouseDown( event ) {
+				event.preventDefault();
 
-			event.preventDefault();
+				if ( _state === STATE.NONE ) {
 
-			if ( _state === STATE.NONE ) {
+					switch ( event.button ) {
 
-				switch ( event.button ) {
+						case scope.mouseButtons.LEFT:
+							_state = STATE.ROTATE;
+							break;
 
-					case scope.mouseButtons.LEFT:
-						_state = STATE.ROTATE;
-						break;
+						case scope.mouseButtons.MIDDLE:
+							_state = STATE.ZOOM;
+							break;
 
-					case scope.mouseButtons.MIDDLE:
-						_state = STATE.ZOOM;
-						break;
+						case scope.mouseButtons.RIGHT:
+							_state = STATE.PAN;
+							break;
 
-					case scope.mouseButtons.RIGHT:
-						_state = STATE.PAN;
-						break;
+						default:
+							_state = STATE.NONE;
 
-					default:
-						_state = STATE.NONE;
+					}
 
 				}
 
-			}
+				const state = _keyState !== STATE.NONE ? _keyState : _state;
 
-			var state = _keyState !== STATE.NONE ? _keyState : _state;
+				if ( state === STATE.ROTATE && ! scope.noRotate ) {
 
-			if ( state === STATE.ROTATE && ! scope.noRotate ) {
+					_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
 
-				_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
+					_movePrev.copy( _moveCurr );
 
-				_movePrev.copy( _moveCurr );
+				} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
 
-			} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
+					_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-				_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+					_zoomEnd.copy( _zoomStart );
 
-				_zoomEnd.copy( _zoomStart );
+				} else if ( state === STATE.PAN && ! scope.noPan ) {
 
-			} else if ( state === STATE.PAN && ! scope.noPan ) {
+					_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-				_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+					_panEnd.copy( _panStart );
 
-				_panEnd.copy( _panStart );
+				}
 
-			}
+				scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
+				scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
+				scope.dispatchEvent( _startEvent );
 
-			scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
-			scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
-			scope.dispatchEvent( startEvent );
+			}
 
-		}
+			function onMouseMove( event ) {
 
-		function onMouseMove( event ) {
+				if ( scope.enabled === false ) return;
+				event.preventDefault();
+				const state = _keyState !== STATE.NONE ? _keyState : _state;
 
-			if ( scope.enabled === false ) return;
-			event.preventDefault();
-			var state = _keyState !== STATE.NONE ? _keyState : _state;
+				if ( state === STATE.ROTATE && ! scope.noRotate ) {
 
-			if ( state === STATE.ROTATE && ! scope.noRotate ) {
+					_movePrev.copy( _moveCurr );
 
-				_movePrev.copy( _moveCurr );
+					_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
 
-				_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
+				} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
 
-			} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
+					_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-				_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+				} else if ( state === STATE.PAN && ! scope.noPan ) {
 
-			} else if ( state === STATE.PAN && ! scope.noPan ) {
+					_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-				_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+				}
 
 			}
 
-		}
+			function onMouseUp( event ) {
 
-		function onMouseUp( event ) {
+				if ( scope.enabled === false ) return;
+				event.preventDefault();
+				_state = STATE.NONE;
+				scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
+				scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+				scope.dispatchEvent( _endEvent );
 
-			if ( scope.enabled === false ) return;
-			event.preventDefault();
-			_state = STATE.NONE;
-			scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
-			scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
-			scope.dispatchEvent( endEvent );
+			}
 
-		}
+			function mousewheel( event ) {
 
-		function mousewheel( event ) {
+				if ( scope.enabled === false ) return;
+				if ( scope.noZoom === true ) return;
+				event.preventDefault();
 
-			if ( scope.enabled === false ) return;
-			if ( scope.noZoom === true ) return;
-			event.preventDefault();
+				switch ( event.deltaMode ) {
 
-			switch ( event.deltaMode ) {
+					case 2:
+					// Zoom in pages
+						_zoomStart.y -= event.deltaY * 0.025;
+						break;
 
-				case 2:
-				// Zoom in pages
-					_zoomStart.y -= event.deltaY * 0.025;
-					break;
+					case 1:
+					// Zoom in lines
+						_zoomStart.y -= event.deltaY * 0.01;
+						break;
 
-				case 1:
-				// Zoom in lines
-					_zoomStart.y -= event.deltaY * 0.01;
-					break;
+					default:
+					// undefined, 0, assume pixels
+						_zoomStart.y -= event.deltaY * 0.00025;
+						break;
+
+				}
 
-				default:
-				// undefined, 0, assume pixels
-					_zoomStart.y -= event.deltaY * 0.00025;
-					break;
+				scope.dispatchEvent( _startEvent );
+				scope.dispatchEvent( _endEvent );
 
 			}
 
-			scope.dispatchEvent( startEvent );
-			scope.dispatchEvent( endEvent );
+			function touchstart( event ) {
 
-		}
+				if ( scope.enabled === false ) return;
+				event.preventDefault();
 
-		function touchstart( event ) {
+				switch ( event.touches.length ) {
 
-			if ( scope.enabled === false ) return;
-			event.preventDefault();
+					case 1:
+						_state = STATE.TOUCH_ROTATE;
 
-			switch ( event.touches.length ) {
+						_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 
-				case 1:
-					_state = STATE.TOUCH_ROTATE;
+						_movePrev.copy( _moveCurr );
 
-					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+						break;
 
-					_movePrev.copy( _moveCurr );
+					default:
+					// 2 or more
+						_state = STATE.TOUCH_ZOOM_PAN;
+						const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+						const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+						_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
+						const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+						const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
 
-					break;
+						_panStart.copy( getMouseOnScreen( x, y ) );
 
-				default:
-				// 2 or more
-					_state = STATE.TOUCH_ZOOM_PAN;
-					var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
-					var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-					_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
-					var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
-					var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+						_panEnd.copy( _panStart );
 
-					_panStart.copy( getMouseOnScreen( x, y ) );
+						break;
 
-					_panEnd.copy( _panStart );
+				}
 
-					break;
+				scope.dispatchEvent( _startEvent );
 
 			}
 
-			scope.dispatchEvent( startEvent );
-
-		}
+			function touchmove( event ) {
 
-		function touchmove( event ) {
+				if ( scope.enabled === false ) return;
+				event.preventDefault();
 
-			if ( scope.enabled === false ) return;
-			event.preventDefault();
+				switch ( event.touches.length ) {
 
-			switch ( event.touches.length ) {
+					case 1:
+						_movePrev.copy( _moveCurr );
 
-				case 1:
-					_movePrev.copy( _moveCurr );
+						_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 
-					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+						break;
 
-					break;
+					default:
+					// 2 or more
+						const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+						const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+						_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
+						const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+						const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
 
-				default:
-				// 2 or more
-					var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
-					var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-					_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
-					var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
-					var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+						_panEnd.copy( getMouseOnScreen( x, y ) );
 
-					_panEnd.copy( getMouseOnScreen( x, y ) );
+						break;
 
-					break;
+				}
 
 			}
 
-		}
+			function touchend( event ) {
 
-		function touchend( event ) {
+				if ( scope.enabled === false ) return;
 
-			if ( scope.enabled === false ) return;
+				switch ( event.touches.length ) {
 
-			switch ( event.touches.length ) {
+					case 0:
+						_state = STATE.NONE;
+						break;
 
-				case 0:
-					_state = STATE.NONE;
-					break;
+					case 1:
+						_state = STATE.TOUCH_ROTATE;
 
-				case 1:
-					_state = STATE.TOUCH_ROTATE;
+						_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 
-					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+						_movePrev.copy( _moveCurr );
 
-					_movePrev.copy( _moveCurr );
+						break;
+
+				}
 
-					break;
+				scope.dispatchEvent( _endEvent );
 
 			}
 
-			scope.dispatchEvent( endEvent );
+			function contextmenu( event ) {
 
-		}
+				if ( scope.enabled === false ) return;
+				event.preventDefault();
 
-		function contextmenu( event ) {
+			}
 
-			if ( scope.enabled === false ) return;
-			event.preventDefault();
+			this.dispose = function () {
 
-		}
+				scope.domElement.removeEventListener( 'contextmenu', contextmenu );
+				scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
+				scope.domElement.removeEventListener( 'wheel', mousewheel );
+				scope.domElement.removeEventListener( 'touchstart', touchstart );
+				scope.domElement.removeEventListener( 'touchend', touchend );
+				scope.domElement.removeEventListener( 'touchmove', touchmove );
+				scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
+				scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+				window.removeEventListener( 'keydown', keydown );
+				window.removeEventListener( 'keyup', keyup );
 
-		this.dispose = function () {
-
-			scope.domElement.removeEventListener( 'contextmenu', contextmenu );
-			scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
-			scope.domElement.removeEventListener( 'wheel', mousewheel );
-			scope.domElement.removeEventListener( 'touchstart', touchstart );
-			scope.domElement.removeEventListener( 'touchend', touchend );
-			scope.domElement.removeEventListener( 'touchmove', touchmove );
-			scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
-			scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
-			window.removeEventListener( 'keydown', keydown );
-			window.removeEventListener( 'keyup', keyup );
-
-		};
-
-		this.domElement.addEventListener( 'contextmenu', contextmenu );
-		this.domElement.addEventListener( 'pointerdown', onPointerDown );
-		this.domElement.addEventListener( 'wheel', mousewheel );
-		this.domElement.addEventListener( 'touchstart', touchstart );
-		this.domElement.addEventListener( 'touchend', touchend );
-		this.domElement.addEventListener( 'touchmove', touchmove );
-		this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
-		this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
-		window.addEventListener( 'keydown', keydown );
-		window.addEventListener( 'keyup', keyup );
-		this.handleResize(); // force an update at start
-
-		this.update();
+			};
 
-	};
+			this.domElement.addEventListener( 'contextmenu', contextmenu );
+			this.domElement.addEventListener( 'pointerdown', onPointerDown );
+			this.domElement.addEventListener( 'wheel', mousewheel );
+			this.domElement.addEventListener( 'touchstart', touchstart );
+			this.domElement.addEventListener( 'touchend', touchend );
+			this.domElement.addEventListener( 'touchmove', touchmove );
+			this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
+			this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
+			window.addEventListener( 'keydown', keydown );
+			window.addEventListener( 'keyup', keyup );
+			this.handleResize(); // force an update at start
+
+			this.update();
+
+		}
 
-	TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
-	TrackballControls.prototype.constructor = TrackballControls;
+	}
 
 	THREE.TrackballControls = TrackballControls;
 

+ 316 - 297
examples/js/controls/TransformControls.js

@@ -669,260 +669,283 @@
 	TransformControls.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
 		constructor: TransformControls,
 		isTransformControls: true
-	} );
+	} ); //
+	// Reusable utility variables
 
-	var TransformControlsGizmo = function () {
+	const _tempVector = new THREE.Vector3( 0, 0, 0 );
 
-		'use strict';
+	const _tempEuler = new THREE.Euler();
 
-		THREE.Object3D.call( this );
-		this.type = 'TransformControlsGizmo'; // shared materials
-
-		var gizmoMaterial = new THREE.MeshBasicMaterial( {
-			depthTest: false,
-			depthWrite: false,
-			transparent: true,
-			side: THREE.DoubleSide,
-			fog: false,
-			toneMapped: false
-		} );
-		var gizmoLineMaterial = new THREE.LineBasicMaterial( {
-			depthTest: false,
-			depthWrite: false,
-			transparent: true,
-			linewidth: 1,
-			fog: false,
-			toneMapped: false
-		} ); // Make unique material for each axis/color
-
-		var matInvisible = gizmoMaterial.clone();
-		matInvisible.opacity = 0.15;
-		var matHelper = gizmoMaterial.clone();
-		matHelper.opacity = 0.33;
-		var matRed = gizmoMaterial.clone();
-		matRed.color.set( 0xff0000 );
-		var matGreen = gizmoMaterial.clone();
-		matGreen.color.set( 0x00ff00 );
-		var matBlue = gizmoMaterial.clone();
-		matBlue.color.set( 0x0000ff );
-		var matWhiteTransparent = gizmoMaterial.clone();
-		matWhiteTransparent.opacity = 0.25;
-		var matYellowTransparent = matWhiteTransparent.clone();
-		matYellowTransparent.color.set( 0xffff00 );
-		var matCyanTransparent = matWhiteTransparent.clone();
-		matCyanTransparent.color.set( 0x00ffff );
-		var matMagentaTransparent = matWhiteTransparent.clone();
-		matMagentaTransparent.color.set( 0xff00ff );
-		var matYellow = gizmoMaterial.clone();
-		matYellow.color.set( 0xffff00 );
-		var matLineRed = gizmoLineMaterial.clone();
-		matLineRed.color.set( 0xff0000 );
-		var matLineGreen = gizmoLineMaterial.clone();
-		matLineGreen.color.set( 0x00ff00 );
-		var matLineBlue = gizmoLineMaterial.clone();
-		matLineBlue.color.set( 0x0000ff );
-		var matLineCyan = gizmoLineMaterial.clone();
-		matLineCyan.color.set( 0x00ffff );
-		var matLineMagenta = gizmoLineMaterial.clone();
-		matLineMagenta.color.set( 0xff00ff );
-		var matLineYellow = gizmoLineMaterial.clone();
-		matLineYellow.color.set( 0xffff00 );
-		var matLineGray = gizmoLineMaterial.clone();
-		matLineGray.color.set( 0x787878 );
-		var matLineYellowTransparent = matLineYellow.clone();
-		matLineYellowTransparent.opacity = 0.25; // reusable geometry
-
-		var arrowGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false );
-		var scaleHandleGeometry = new THREE.BoxGeometry( 0.125, 0.125, 0.125 );
-		var lineGeometry = new THREE.BufferGeometry();
-		lineGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
-
-		var CircleGeometry = function ( radius, arc ) {
-
-			var geometry = new THREE.BufferGeometry();
-			var vertices = [];
-
-			for ( var i = 0; i <= 64 * arc; ++ i ) {
-
-				vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
+	const _alignVector = new THREE.Vector3( 0, 1, 0 );
 
-			}
+	const _zeroVector = new THREE.Vector3( 0, 0, 0 );
 
-			geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-			return geometry;
+	const _lookAtMatrix = new THREE.Matrix4();
 
-		}; // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
+	const _tempQuaternion = new THREE.Quaternion();
 
+	const _tempQuaternion2 = new THREE.Quaternion();
 
-		var TranslateHelperGeometry = function () {
+	const _identityQuaternion = new THREE.Quaternion();
 
-			var geometry = new THREE.BufferGeometry();
-			geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
-			return geometry;
+	const _dirVector = new THREE.Vector3();
 
-		}; // Gizmo definitions - custom hierarchy definitions for setupGizmo() function
+	const _tempMatrix = new THREE.Matrix4();
 
+	const _unitX = new THREE.Vector3( 1, 0, 0 );
 
-		var gizmoTranslate = {
-			X: [[ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, - Math.PI / 2 ], null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineRed ) ]],
-			Y: [[ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ]]],
-			Z: [[ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ - Math.PI / 2, 0, 0 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ]]],
-			XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ], [ 0, 0, 0 ]]],
-			XY: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matYellowTransparent.clone() ), [ 0.15, 0.15, 0 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]],
-			YZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matCyanTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
-			XZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matMagentaTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]]
-		};
-		var pickerTranslate = {
-			X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
-			Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ]]],
-			Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ]]],
-			XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), matInvisible ) ]],
-			XY: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ]]],
-			YZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ]]],
-			XZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ]]]
-		};
-		var helperTranslate = {
-			START: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
-			END: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
-			DELTA: [[ new THREE.Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]],
-			X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
-			Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
-			Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
-		};
-		var gizmoRotate = {
-			X: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineRed ) ], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ]]],
-			Y: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ]]],
-			Z: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ]], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ]]],
-			E: [[ new THREE.Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, - Math.PI / 2 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ - 1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, - 1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]]],
-			XYZE: [[ new THREE.Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ]]]
-		};
-		var helperRotate = {
-			AXIS: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]]
-		};
-		var pickerRotate = {
-			X: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]]],
-			Y: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]]],
-			Z: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
-			E: [[ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ]],
-			XYZE: [[ new THREE.Mesh( new THREE.SphereGeometry( 0.7, 10, 8 ), matInvisible ) ]]
-		};
-		var gizmoScale = {
-			X: [[ new THREE.Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ]]],
-			Y: [[ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ]], [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ]]],
-			Z: [[ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ], [ 0.8, 1, 1 ]]],
-			XY: [[ new THREE.Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]],
-			YZ: [[ new THREE.Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
-			XZ: [[ new THREE.Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
-			XYZX: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 1.1, 0, 0 ]]],
-			XYZY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 1.1, 0 ]]],
-			XYZZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 0, 1.1 ]]]
-		};
-		var pickerScale = {
-			X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
-			Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ]]],
-			Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]]],
-			XY: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ]]],
-			YZ: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ]]],
-			XZ: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ]]],
-			XYZX: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ]]],
-			XYZY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ]]],
-			XYZZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ]]]
-		};
-		var helperScale = {
-			X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
-			Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
-			Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
-		}; // Creates an THREE.Object3D with gizmos described in custom hierarchy definition.
+	const _unitY = new THREE.Vector3( 0, 1, 0 );
 
-		var setupGizmo = function ( gizmoMap ) {
+	const _unitZ = new THREE.Vector3( 0, 0, 1 );
 
-			var gizmo = new THREE.Object3D();
+	const _v1 = new THREE.Vector3();
 
-			for ( var name in gizmoMap ) {
+	const _v2 = new THREE.Vector3();
 
-				for ( var i = gizmoMap[ name ].length; i --; ) {
+	const _v3 = new THREE.Vector3();
 
-					var object = gizmoMap[ name ][ i ][ 0 ].clone();
-					var position = gizmoMap[ name ][ i ][ 1 ];
-					var rotation = gizmoMap[ name ][ i ][ 2 ];
-					var scale = gizmoMap[ name ][ i ][ 3 ];
-					var tag = gizmoMap[ name ][ i ][ 4 ]; // name and tag properties are essential for picking and updating logic.
+	class TransformControlsGizmo extends THREE.Object3D {
 
-					object.name = name;
-					object.tag = tag;
+		constructor() {
 
-					if ( position ) {
+			super();
+			this.type = 'TransformControlsGizmo'; // shared materials
 
-						object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
+			const gizmoMaterial = new THREE.MeshBasicMaterial( {
+				depthTest: false,
+				depthWrite: false,
+				transparent: true,
+				side: THREE.DoubleSide,
+				fog: false,
+				toneMapped: false
+			} );
+			const gizmoLineMaterial = new THREE.LineBasicMaterial( {
+				depthTest: false,
+				depthWrite: false,
+				transparent: true,
+				linewidth: 1,
+				fog: false,
+				toneMapped: false
+			} ); // Make unique material for each axis/color
+
+			const matInvisible = gizmoMaterial.clone();
+			matInvisible.opacity = 0.15;
+			const matHelper = gizmoMaterial.clone();
+			matHelper.opacity = 0.33;
+			const matRed = gizmoMaterial.clone();
+			matRed.color.set( 0xff0000 );
+			const matGreen = gizmoMaterial.clone();
+			matGreen.color.set( 0x00ff00 );
+			const matBlue = gizmoMaterial.clone();
+			matBlue.color.set( 0x0000ff );
+			const matWhiteTransparent = gizmoMaterial.clone();
+			matWhiteTransparent.opacity = 0.25;
+			const matYellowTransparent = matWhiteTransparent.clone();
+			matYellowTransparent.color.set( 0xffff00 );
+			const matCyanTransparent = matWhiteTransparent.clone();
+			matCyanTransparent.color.set( 0x00ffff );
+			const matMagentaTransparent = matWhiteTransparent.clone();
+			matMagentaTransparent.color.set( 0xff00ff );
+			const matYellow = gizmoMaterial.clone();
+			matYellow.color.set( 0xffff00 );
+			const matLineRed = gizmoLineMaterial.clone();
+			matLineRed.color.set( 0xff0000 );
+			const matLineGreen = gizmoLineMaterial.clone();
+			matLineGreen.color.set( 0x00ff00 );
+			const matLineBlue = gizmoLineMaterial.clone();
+			matLineBlue.color.set( 0x0000ff );
+			const matLineCyan = gizmoLineMaterial.clone();
+			matLineCyan.color.set( 0x00ffff );
+			const matLineMagenta = gizmoLineMaterial.clone();
+			matLineMagenta.color.set( 0xff00ff );
+			const matLineYellow = gizmoLineMaterial.clone();
+			matLineYellow.color.set( 0xffff00 );
+			const matLineGray = gizmoLineMaterial.clone();
+			matLineGray.color.set( 0x787878 );
+			const matLineYellowTransparent = matLineYellow.clone();
+			matLineYellowTransparent.opacity = 0.25; // reusable geometry
+
+			const arrowGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false );
+			const scaleHandleGeometry = new THREE.BoxGeometry( 0.125, 0.125, 0.125 );
+			const lineGeometry = new THREE.BufferGeometry();
+			lineGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
+
+			function CircleGeometry( radius, arc ) {
+
+				const geometry = new THREE.BufferGeometry();
+				const vertices = [];
+
+				for ( let i = 0; i <= 64 * arc; ++ i ) {
+
+					vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
 
-					}
+				}
 
-					if ( rotation ) {
+				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+				return geometry;
+
+			} // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
+
+
+			function TranslateHelperGeometry() {
+
+				const geometry = new THREE.BufferGeometry();
+				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
+				return geometry;
+
+			} // Gizmo definitions - custom hierarchy definitions for setupGizmo() function
+
+
+			const gizmoTranslate = {
+				X: [[ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, - Math.PI / 2 ], null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineRed ) ]],
+				Y: [[ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ]]],
+				Z: [[ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ], [ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ - Math.PI / 2, 0, 0 ], null, 'bwd' ], [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ]]],
+				XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ], [ 0, 0, 0 ]]],
+				XY: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matYellowTransparent.clone() ), [ 0.15, 0.15, 0 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]],
+				YZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matCyanTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
+				XZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.295, 0.295 ), matMagentaTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]]
+			};
+			const pickerTranslate = {
+				X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
+				Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ]]],
+				Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ]]],
+				XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), matInvisible ) ]],
+				XY: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ]]],
+				YZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ]]],
+				XZ: [[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ]]]
+			};
+			const helperTranslate = {
+				START: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
+				END: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
+				DELTA: [[ new THREE.Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]],
+				X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
+				Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
+				Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
+			};
+			const gizmoRotate = {
+				X: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineRed ) ], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ]]],
+				Y: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ]]],
+				Z: [[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ]], [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ]]],
+				E: [[ new THREE.Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, - Math.PI / 2 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ - 1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, - 1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]]],
+				XYZE: [[ new THREE.Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ]]]
+			};
+			const helperRotate = {
+				AXIS: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]]
+			};
+			const pickerRotate = {
+				X: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]]],
+				Y: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]]],
+				Z: [[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
+				E: [[ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ]],
+				XYZE: [[ new THREE.Mesh( new THREE.SphereGeometry( 0.7, 10, 8 ), matInvisible ) ]]
+			};
+			var gizmoScale = {
+				X: [[ new THREE.Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ]]],
+				Y: [[ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ]], [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ]]],
+				Z: [[ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ], [ 0.8, 1, 1 ]]],
+				XY: [[ new THREE.Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]],
+				YZ: [[ new THREE.Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
+				XZ: [[ new THREE.Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ]], [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]],
+				XYZX: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 1.1, 0, 0 ]]],
+				XYZY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 1.1, 0 ]]],
+				XYZZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 0, 1.1 ]]]
+			};
+			const pickerScale = {
+				X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
+				Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ]]],
+				Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]]],
+				XY: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ]]],
+				YZ: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ]]],
+				XZ: [[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ]]],
+				XYZX: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ]]],
+				XYZY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ]]],
+				XYZZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ]]]
+			};
+			const helperScale = {
+				X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
+				Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
+				Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
+			}; // Creates an THREE.Object3D with gizmos described in custom hierarchy definition.
+
+			function setupGizmo( gizmoMap ) {
+
+				const gizmo = new THREE.Object3D();
+
+				for ( const name in gizmoMap ) {
+
+					for ( let i = gizmoMap[ name ].length; i --; ) {
+
+						const object = gizmoMap[ name ][ i ][ 0 ].clone();
+						const position = gizmoMap[ name ][ i ][ 1 ];
+						const rotation = gizmoMap[ name ][ i ][ 2 ];
+						const scale = gizmoMap[ name ][ i ][ 3 ];
+						const tag = gizmoMap[ name ][ i ][ 4 ]; // name and tag properties are essential for picking and updating logic.
+
+						object.name = name;
+						object.tag = tag;
+
+						if ( position ) {
+
+							object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
 
-						object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
+						}
 
-					}
+						if ( rotation ) {
 
-					if ( scale ) {
+							object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
 
-						object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] );
+						}
 
-					}
+						if ( scale ) {
 
-					object.updateMatrix();
-					var tempGeometry = object.geometry.clone();
-					tempGeometry.applyMatrix4( object.matrix );
-					object.geometry = tempGeometry;
-					object.renderOrder = Infinity;
-					object.position.set( 0, 0, 0 );
-					object.rotation.set( 0, 0, 0 );
-					object.scale.set( 1, 1, 1 );
-					gizmo.add( object );
+							object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] );
+
+						}
+
+						object.updateMatrix();
+						const tempGeometry = object.geometry.clone();
+						tempGeometry.applyMatrix4( object.matrix );
+						object.geometry = tempGeometry;
+						object.renderOrder = Infinity;
+						object.position.set( 0, 0, 0 );
+						object.rotation.set( 0, 0, 0 );
+						object.scale.set( 1, 1, 1 );
+						gizmo.add( object );
+
+					}
 
 				}
 
-			}
+				return gizmo;
 
-			return gizmo;
+			} // Gizmo creation
 
-		}; // Reusable utility variables
 
+			this.gizmo = {};
+			this.picker = {};
+			this.helper = {};
+			this.add( this.gizmo[ 'translate' ] = setupGizmo( gizmoTranslate ) );
+			this.add( this.gizmo[ 'rotate' ] = setupGizmo( gizmoRotate ) );
+			this.add( this.gizmo[ 'scale' ] = setupGizmo( gizmoScale ) );
+			this.add( this.picker[ 'translate' ] = setupGizmo( pickerTranslate ) );
+			this.add( this.picker[ 'rotate' ] = setupGizmo( pickerRotate ) );
+			this.add( this.picker[ 'scale' ] = setupGizmo( pickerScale ) );
+			this.add( this.helper[ 'translate' ] = setupGizmo( helperTranslate ) );
+			this.add( this.helper[ 'rotate' ] = setupGizmo( helperRotate ) );
+			this.add( this.helper[ 'scale' ] = setupGizmo( helperScale ) ); // Pickers should be hidden always
 
-		var tempVector = new THREE.Vector3( 0, 0, 0 );
-		var tempEuler = new THREE.Euler();
-		var alignVector = new THREE.Vector3( 0, 1, 0 );
-		var zeroVector = new THREE.Vector3( 0, 0, 0 );
-		var lookAtMatrix = new THREE.Matrix4();
-		var tempQuaternion = new THREE.Quaternion();
-		var tempQuaternion2 = new THREE.Quaternion();
-		var identityQuaternion = new THREE.Quaternion();
-		var unitX = new THREE.Vector3( 1, 0, 0 );
-		var unitY = new THREE.Vector3( 0, 1, 0 );
-		var unitZ = new THREE.Vector3( 0, 0, 1 ); // Gizmo creation
-
-		this.gizmo = {};
-		this.picker = {};
-		this.helper = {};
-		this.add( this.gizmo[ 'translate' ] = setupGizmo( gizmoTranslate ) );
-		this.add( this.gizmo[ 'rotate' ] = setupGizmo( gizmoRotate ) );
-		this.add( this.gizmo[ 'scale' ] = setupGizmo( gizmoScale ) );
-		this.add( this.picker[ 'translate' ] = setupGizmo( pickerTranslate ) );
-		this.add( this.picker[ 'rotate' ] = setupGizmo( pickerRotate ) );
-		this.add( this.picker[ 'scale' ] = setupGizmo( pickerScale ) );
-		this.add( this.helper[ 'translate' ] = setupGizmo( helperTranslate ) );
-		this.add( this.helper[ 'rotate' ] = setupGizmo( helperRotate ) );
-		this.add( this.helper[ 'scale' ] = setupGizmo( helperScale ) ); // Pickers should be hidden always
-
-		this.picker[ 'translate' ].visible = false;
-		this.picker[ 'rotate' ].visible = false;
-		this.picker[ 'scale' ].visible = false; // updateMatrixWorld will update transformations and appearance of individual handles
+			this.picker[ 'translate' ].visible = false;
+			this.picker[ 'rotate' ].visible = false;
+			this.picker[ 'scale' ].visible = false;
 
-		this.updateMatrixWorld = function () {
+		} // updateMatrixWorld will update transformations and appearance of individual handles
 
-			var space = this.space;
-			if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
 
-			var quaternion = space === 'local' ? this.worldQuaternion : identityQuaternion; // Show only gizmos for current transform mode
+		updateMatrixWorld( force ) {
+
+			const space = this.mode === 'scale' ? this.space : 'local'; // scale always oriented to local rotation
+
+			const quaternion = space === 'local' ? this.worldQuaternion : _identityQuaternion; // Show only gizmos for current transform mode
 
 			this.gizmo[ 'translate' ].visible = this.mode === 'translate';
 			this.gizmo[ 'rotate' ].visible = this.mode === 'rotate';
@@ -930,19 +953,19 @@
 			this.helper[ 'translate' ].visible = this.mode === 'translate';
 			this.helper[ 'rotate' ].visible = this.mode === 'rotate';
 			this.helper[ 'scale' ].visible = this.mode === 'scale';
-			var handles = [];
+			let handles = [];
 			handles = handles.concat( this.picker[ this.mode ].children );
 			handles = handles.concat( this.gizmo[ this.mode ].children );
 			handles = handles.concat( this.helper[ this.mode ].children );
 
-			for ( var i = 0; i < handles.length; i ++ ) {
+			for ( let i = 0; i < handles.length; i ++ ) {
 
-				var handle = handles[ i ]; // hide aligned to camera
+				const handle = handles[ i ]; // hide aligned to camera
 
 				handle.visible = true;
 				handle.rotation.set( 0, 0, 0 );
 				handle.position.copy( this.worldPosition );
-				var factor;
+				let factor;
 
 				if ( this.camera.isOrthographicCamera ) {
 
@@ -967,10 +990,11 @@
 
 						if ( this.axis === 'X' ) {
 
-							tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) );
-							handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
+							_tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, 0 ) );
 
-							if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
+
+							if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
 
 								handle.visible = false;
 
@@ -980,10 +1004,11 @@
 
 						if ( this.axis === 'Y' ) {
 
-							tempQuaternion.setFromEuler( tempEuler.set( 0, 0, Math.PI / 2 ) );
-							handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
+							_tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, Math.PI / 2 ) );
+
+							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
 
-							if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
 
 								handle.visible = false;
 
@@ -993,10 +1018,11 @@
 
 						if ( this.axis === 'Z' ) {
 
-							tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
-							handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
+							_tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
 
-							if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
+
+							if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
 
 								handle.visible = false;
 
@@ -1006,10 +1032,12 @@
 
 						if ( this.axis === 'XYZE' ) {
 
-							tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
-							alignVector.copy( this.rotationAxis );
-							handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( zeroVector, alignVector, unitY ) );
-							handle.quaternion.multiply( tempQuaternion );
+							_tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
+
+							_alignVector.copy( this.rotationAxis );
+
+							handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( _zeroVector, _alignVector, _unitY ) );
+							handle.quaternion.multiply( _tempQuaternion );
 							handle.visible = this.dragging;
 
 						}
@@ -1034,9 +1062,12 @@
 
 						handle.position.copy( this.worldPositionStart );
 						handle.quaternion.copy( this.worldQuaternionStart );
-						tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 );
-						tempVector.applyQuaternion( this.worldQuaternionStart.clone().invert() );
-						handle.scale.copy( tempVector );
+
+						_tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 );
+
+						_tempVector.applyQuaternion( this.worldQuaternionStart.clone().invert() );
+
+						handle.scale.copy( _tempVector );
 						handle.visible = this.dragging;
 
 					} else {
@@ -1072,13 +1103,13 @@
 				if ( this.mode === 'translate' || this.mode === 'scale' ) {
 
 					// Hide translate and scale axis facing the camera
-					var AXIS_HIDE_TRESHOLD = 0.99;
-					var PLANE_HIDE_TRESHOLD = 0.2;
-					var AXIS_FLIP_TRESHOLD = 0.0;
+					const AXIS_HIDE_TRESHOLD = 0.99;
+					const PLANE_HIDE_TRESHOLD = 0.2;
+					const AXIS_FLIP_TRESHOLD = 0.0;
 
 					if ( handle.name === 'X' || handle.name === 'XYZX' ) {
 
-						if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1089,7 +1120,7 @@
 
 					if ( handle.name === 'Y' || handle.name === 'XYZY' ) {
 
-						if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1100,7 +1131,7 @@
 
 					if ( handle.name === 'Z' || handle.name === 'XYZZ' ) {
 
-						if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1111,7 +1142,7 @@
 
 					if ( handle.name === 'XY' ) {
 
-						if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1122,7 +1153,7 @@
 
 					if ( handle.name === 'YZ' ) {
 
-						if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1133,7 +1164,7 @@
 
 					if ( handle.name === 'XZ' ) {
 
-						if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
 
 							handle.scale.set( 1e-10, 1e-10, 1e-10 );
 							handle.visible = false;
@@ -1145,7 +1176,7 @@
 
 					if ( handle.name.search( 'X' ) !== - 1 ) {
 
-						if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
 
 							if ( handle.tag === 'fwd' ) {
 
@@ -1167,7 +1198,7 @@
 
 					if ( handle.name.search( 'Y' ) !== - 1 ) {
 
-						if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
 
 							if ( handle.tag === 'fwd' ) {
 
@@ -1189,7 +1220,7 @@
 
 					if ( handle.name.search( 'Z' ) !== - 1 ) {
 
-						if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
 
 							if ( handle.tag === 'fwd' ) {
 
@@ -1212,36 +1243,43 @@
 				} else if ( this.mode === 'rotate' ) {
 
 					// Align handles to current local or world rotation
-					tempQuaternion2.copy( quaternion );
-					alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).invert() );
+					_tempQuaternion2.copy( quaternion );
+
+					_alignVector.copy( this.eye ).applyQuaternion( _tempQuaternion.copy( quaternion ).invert() );
 
 					if ( handle.name.search( 'E' ) !== - 1 ) {
 
-						handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( this.eye, zeroVector, unitY ) );
+						handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( this.eye, _zeroVector, _unitY ) );
 
 					}
 
 					if ( handle.name === 'X' ) {
 
-						tempQuaternion.setFromAxisAngle( unitX, Math.atan2( - alignVector.y, alignVector.z ) );
-						tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
-						handle.quaternion.copy( tempQuaternion );
+						_tempQuaternion.setFromAxisAngle( _unitX, Math.atan2( - _alignVector.y, _alignVector.z ) );
+
+						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
+
+						handle.quaternion.copy( _tempQuaternion );
 
 					}
 
 					if ( handle.name === 'Y' ) {
 
-						tempQuaternion.setFromAxisAngle( unitY, Math.atan2( alignVector.x, alignVector.z ) );
-						tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
-						handle.quaternion.copy( tempQuaternion );
+						_tempQuaternion.setFromAxisAngle( _unitY, Math.atan2( _alignVector.x, _alignVector.z ) );
+
+						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
+
+						handle.quaternion.copy( _tempQuaternion );
 
 					}
 
 					if ( handle.name === 'Z' ) {
 
-						tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) );
-						tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
-						handle.quaternion.copy( tempQuaternion );
+						_tempQuaternion.setFromAxisAngle( _unitZ, Math.atan2( _alignVector.y, _alignVector.x ) );
+
+						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
+
+						handle.quaternion.copy( _tempQuaternion );
 
 					}
 
@@ -1290,32 +1328,13 @@
 
 			}
 
-			THREE.Object3D.prototype.updateMatrixWorld.call( this );
-
-		};
-
-	};
-
-	TransformControlsGizmo.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
-		constructor: TransformControlsGizmo,
-		isTransformControlsGizmo: true
-	} );
-
-	const _unitX = new THREE.Vector3( 1, 0, 0 );
-
-	const _unitY = new THREE.Vector3( 0, 1, 0 );
-
-	const _unitZ = new THREE.Vector3( 0, 0, 1 );
-
-	const _tempVector = new THREE.Vector3();
-
-	const _dirVector = new THREE.Vector3();
+			super.updateMatrixWorld( force );
 
-	const _alignVector = new THREE.Vector3();
+		}
 
-	const _tempMatrix = new THREE.Matrix4();
+	}
 
-	const _identityQuaternion = new THREE.Quaternion();
+	TransformControlsGizmo.prototype.isTransformControlsGizmo = true; //
 
 	class TransformControlsPlane extends THREE.Mesh {
 
@@ -1339,14 +1358,14 @@
 			this.position.copy( this.worldPosition );
 			if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
 
-			_unitX.set( 1, 0, 0 ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
+			_v1.copy( _unitX ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
 
-			_unitY.set( 0, 1, 0 ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
+			_v2.copy( _unitY ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
 
-			_unitZ.set( 0, 0, 1 ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion ); // Align the plane for current transform mode, axis and space.
+			_v3.copy( _unitZ ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion ); // Align the plane for current transform mode, axis and space.
 
 
-			_alignVector.copy( _unitY );
+			_alignVector.copy( _v2 );
 
 			switch ( this.mode ) {
 
@@ -1355,40 +1374,40 @@
 					switch ( this.axis ) {
 
 						case 'X':
-							_alignVector.copy( this.eye ).cross( _unitX );
+							_alignVector.copy( this.eye ).cross( _v1 );
 
-							_dirVector.copy( _unitX ).cross( _alignVector );
+							_dirVector.copy( _v1 ).cross( _alignVector );
 
 							break;
 
 						case 'Y':
-							_alignVector.copy( this.eye ).cross( _unitY );
+							_alignVector.copy( this.eye ).cross( _v2 );
 
-							_dirVector.copy( _unitY ).cross( _alignVector );
+							_dirVector.copy( _v2 ).cross( _alignVector );
 
 							break;
 
 						case 'Z':
-							_alignVector.copy( this.eye ).cross( _unitZ );
+							_alignVector.copy( this.eye ).cross( _v3 );
 
-							_dirVector.copy( _unitZ ).cross( _alignVector );
+							_dirVector.copy( _v3 ).cross( _alignVector );
 
 							break;
 
 						case 'XY':
-							_dirVector.copy( _unitZ );
+							_dirVector.copy( _v3 );
 
 							break;
 
 						case 'YZ':
-							_dirVector.copy( _unitX );
+							_dirVector.copy( _v1 );
 
 							break;
 
 						case 'XZ':
-							_alignVector.copy( _unitZ );
+							_alignVector.copy( _v3 );
 
-							_dirVector.copy( _unitY );
+							_dirVector.copy( _v2 );
 
 							break;
 

+ 68 - 80
examples/jsm/controls/DeviceOrientationControls.js

@@ -6,139 +6,131 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-/**
- * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
- */
+const _zee = new Vector3( 0, 0, 1 );
+const _euler = new Euler();
+const _q0 = new Quaternion();
+const _q1 = new Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
 
-var DeviceOrientationControls = function ( object ) {
+const _changeEvent = { type: 'change' };
 
-	if ( window.isSecureContext === false ) {
+class DeviceOrientationControls extends EventDispatcher {
 
-		console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
+	constructor( object ) {
 
-	}
-
-	var scope = this;
-	var changeEvent = { type: 'change' };
-	var EPS = 0.000001;
+		super();
 
-	this.object = object;
-	this.object.rotation.reorder( 'YXZ' );
+		if ( window.isSecureContext === false ) {
 
-	this.enabled = true;
+			console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
 
-	this.deviceOrientation = {};
-	this.screenOrientation = 0;
+		}
 
-	this.alphaOffset = 0; // radians
+		const scope = this;
 
-	var onDeviceOrientationChangeEvent = function ( event ) {
+		const EPS = 0.000001;
+		const lastQuaternion = new Quaternion();
 
-		scope.deviceOrientation = event;
+		this.object = object;
+		this.object.rotation.reorder( 'YXZ' );
 
-	};
+		this.enabled = true;
 
-	var onScreenOrientationChangeEvent = function () {
+		this.deviceOrientation = {};
+		this.screenOrientation = 0;
 
-		scope.screenOrientation = window.orientation || 0;
+		this.alphaOffset = 0; // radians
 
-	};
+		const onDeviceOrientationChangeEvent = function ( event ) {
 
-	// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
+			scope.deviceOrientation = event;
 
-	var setObjectQuaternion = function () {
+		};
 
-		var zee = new Vector3( 0, 0, 1 );
+		const onScreenOrientationChangeEvent = function () {
 
-		var euler = new Euler();
+			scope.screenOrientation = window.orientation || 0;
 
-		var q0 = new Quaternion();
+		};
 
-		var q1 = new Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
+		// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
 
-		return function ( quaternion, alpha, beta, gamma, orient ) {
+		const setObjectQuaternion = function ( quaternion, alpha, beta, gamma, orient ) {
 
-			euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
+			_euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
 
-			quaternion.setFromEuler( euler ); // orient the device
+			quaternion.setFromEuler( _euler ); // orient the device
 
-			quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
+			quaternion.multiply( _q1 ); // camera looks out the back of the device, not the top
 
-			quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
+			quaternion.multiply( _q0.setFromAxisAngle( _zee, - orient ) ); // adjust for screen orientation
 
 		};
 
-	}();
+		this.connect = function () {
 
-	this.connect = function () {
+			onScreenOrientationChangeEvent(); // run once on load
 
-		onScreenOrientationChangeEvent(); // run once on load
+			// iOS 13+
 
-		// iOS 13+
+			if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
 
-		if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
+				window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
 
-			window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
+					if ( response == 'granted' ) {
 
-				if ( response == 'granted' ) {
+						window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+						window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
 
-					window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-					window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
+					}
 
-				}
-
-			} ).catch( function ( error ) {
+				} ).catch( function ( error ) {
 
-				console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
+					console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
 
-			} );
+				} );
 
-		} else {
+			} else {
 
-			window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-			window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-		}
+				window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+				window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
 
-		scope.enabled = true;
-
-	};
+			}
 
-	this.disconnect = function () {
+			scope.enabled = true;
 
-		window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-		window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
+		};
 
-		scope.enabled = false;
+		this.disconnect = function () {
 
-	};
+			window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
+			window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
 
-	this.update = ( function () {
+			scope.enabled = false;
 
-		var lastQuaternion = new Quaternion();
+		};
 
-		return function () {
+		this.update = function () {
 
 			if ( scope.enabled === false ) return;
 
-			var device = scope.deviceOrientation;
+			const device = scope.deviceOrientation;
 
 			if ( device ) {
 
-				var alpha = device.alpha ? MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
+				const alpha = device.alpha ? MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
 
-				var beta = device.beta ? MathUtils.degToRad( device.beta ) : 0; // X'
+				const beta = device.beta ? MathUtils.degToRad( device.beta ) : 0; // X'
 
-				var gamma = device.gamma ? MathUtils.degToRad( device.gamma ) : 0; // Y''
+				const gamma = device.gamma ? MathUtils.degToRad( device.gamma ) : 0; // Y''
 
-				var orient = scope.screenOrientation ? MathUtils.degToRad( scope.screenOrientation ) : 0; // O
+				const orient = scope.screenOrientation ? MathUtils.degToRad( scope.screenOrientation ) : 0; // O
 
 				setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
 
 				if ( 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
 
 					lastQuaternion.copy( scope.object.quaternion );
-					scope.dispatchEvent( changeEvent );
+					scope.dispatchEvent( _changeEvent );
 
 				}
 
@@ -146,20 +138,16 @@ var DeviceOrientationControls = function ( object ) {
 
 		};
 
+		this.dispose = function () {
 
-	} )();
+			scope.disconnect();
 
-	this.dispose = function () {
-
-		scope.disconnect();
-
-	};
+		};
 
-	this.connect();
+		this.connect();
 
-};
+	}
 
-DeviceOrientationControls.prototype = Object.create( EventDispatcher.prototype );
-DeviceOrientationControls.prototype.constructor = DeviceOrientationControls;
+}
 
 export { DeviceOrientationControls };

+ 177 - 173
examples/jsm/controls/DragControls.js

@@ -7,321 +7,325 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-var DragControls = function ( _objects, _camera, _domElement ) {
+const _plane = new Plane();
+const _raycaster = new Raycaster();
 
-	var _plane = new Plane();
-	var _raycaster = new Raycaster();
+const _mouse = new Vector2();
+const _offset = new Vector3();
+const _intersection = new Vector3();
+const _worldPosition = new Vector3();
+const _inverseMatrix = new Matrix4();
 
-	var _mouse = new Vector2();
-	var _offset = new Vector3();
-	var _intersection = new Vector3();
-	var _worldPosition = new Vector3();
-	var _inverseMatrix = new Matrix4();
-	var _intersections = [];
+class DragControls extends EventDispatcher {
 
-	var _selected = null, _hovered = null;
+	constructor( _objects, _camera, _domElement ) {
 
-	//
+		super();
 
-	var scope = this;
+		let _selected = null, _hovered = null;
 
-	function activate() {
+		const _intersections = [];
 
-		_domElement.addEventListener( 'pointermove', onPointerMove );
-		_domElement.addEventListener( 'pointerdown', onPointerDown );
-		_domElement.addEventListener( 'pointerup', onPointerCancel );
-		_domElement.addEventListener( 'pointerleave', onPointerCancel );
-		_domElement.addEventListener( 'touchmove', onTouchMove );
-		_domElement.addEventListener( 'touchstart', onTouchStart );
-		_domElement.addEventListener( 'touchend', onTouchEnd );
+		//
 
-	}
+		const scope = this;
 
-	function deactivate() {
+		function activate() {
 
-		_domElement.removeEventListener( 'pointermove', onPointerMove );
-		_domElement.removeEventListener( 'pointerdown', onPointerDown );
-		_domElement.removeEventListener( 'pointerup', onPointerCancel );
-		_domElement.removeEventListener( 'pointerleave', onPointerCancel );
-		_domElement.removeEventListener( 'touchmove', onTouchMove );
-		_domElement.removeEventListener( 'touchstart', onTouchStart );
-		_domElement.removeEventListener( 'touchend', onTouchEnd );
+			_domElement.addEventListener( 'pointermove', onPointerMove );
+			_domElement.addEventListener( 'pointerdown', onPointerDown );
+			_domElement.addEventListener( 'pointerup', onPointerCancel );
+			_domElement.addEventListener( 'pointerleave', onPointerCancel );
+			_domElement.addEventListener( 'touchmove', onTouchMove );
+			_domElement.addEventListener( 'touchstart', onTouchStart );
+			_domElement.addEventListener( 'touchend', onTouchEnd );
 
-		_domElement.style.cursor = '';
+		}
 
-	}
+		function deactivate() {
 
-	function dispose() {
+			_domElement.removeEventListener( 'pointermove', onPointerMove );
+			_domElement.removeEventListener( 'pointerdown', onPointerDown );
+			_domElement.removeEventListener( 'pointerup', onPointerCancel );
+			_domElement.removeEventListener( 'pointerleave', onPointerCancel );
+			_domElement.removeEventListener( 'touchmove', onTouchMove );
+			_domElement.removeEventListener( 'touchstart', onTouchStart );
+			_domElement.removeEventListener( 'touchend', onTouchEnd );
 
-		deactivate();
+			_domElement.style.cursor = '';
 
-	}
+		}
 
-	function getObjects() {
+		function dispose() {
 
-		return _objects;
+			deactivate();
 
-	}
+		}
+
+		function getObjects() {
+
+			return _objects;
+
+		}
 
-	function onPointerMove( event ) {
+		function onPointerMove( event ) {
 
-		event.preventDefault();
+			event.preventDefault();
 
-		switch ( event.pointerType ) {
+			switch ( event.pointerType ) {
 
-			case 'mouse':
-			case 'pen':
-				onMouseMove( event );
-				break;
+				case 'mouse':
+				case 'pen':
+					onMouseMove( event );
+					break;
 
-			// TODO touch
+				// TODO touch
+
+			}
 
 		}
 
-	}
+		function onMouseMove( event ) {
 
-	function onMouseMove( event ) {
+			const rect = _domElement.getBoundingClientRect();
 
-		var rect = _domElement.getBoundingClientRect();
+			_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
-		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
-		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+			_raycaster.setFromCamera( _mouse, _camera );
 
-		_raycaster.setFromCamera( _mouse, _camera );
+			if ( _selected && scope.enabled ) {
 
-		if ( _selected && scope.enabled ) {
+				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+					_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
 
-				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+				}
 
-			}
+				scope.dispatchEvent( { type: 'drag', object: _selected } );
 
-			scope.dispatchEvent( { type: 'drag', object: _selected } );
+				return;
 
-			return;
+			}
 
-		}
+			_intersections.length = 0;
 
-		_intersections.length = 0;
+			_raycaster.setFromCamera( _mouse, _camera );
+			_raycaster.intersectObjects( _objects, true, _intersections );
 
-		_raycaster.setFromCamera( _mouse, _camera );
-		_raycaster.intersectObjects( _objects, true, _intersections );
+			if ( _intersections.length > 0 ) {
 
-		if ( _intersections.length > 0 ) {
+				const object = _intersections[ 0 ].object;
 
-			var object = _intersections[ 0 ].object;
+				_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
 
-			_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
+				if ( _hovered !== object && _hovered !== null ) {
 
-			if ( _hovered !== object && _hovered !== null ) {
+					scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
 
-				scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
+					_domElement.style.cursor = 'auto';
+					_hovered = null;
 
-				_domElement.style.cursor = 'auto';
-				_hovered = null;
+				}
 
-			}
+				if ( _hovered !== object ) {
 
-			if ( _hovered !== object ) {
+					scope.dispatchEvent( { type: 'hoveron', object: object } );
 
-				scope.dispatchEvent( { type: 'hoveron', object: object } );
+					_domElement.style.cursor = 'pointer';
+					_hovered = object;
 
-				_domElement.style.cursor = 'pointer';
-				_hovered = object;
+				}
 
-			}
+			} else {
 
-		} else {
+				if ( _hovered !== null ) {
 
-			if ( _hovered !== null ) {
+					scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
 
-				scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
+					_domElement.style.cursor = 'auto';
+					_hovered = null;
 
-				_domElement.style.cursor = 'auto';
-				_hovered = null;
+				}
 
 			}
 
 		}
 
-	}
+		function onPointerDown( event ) {
 
-	function onPointerDown( event ) {
+			event.preventDefault();
 
-		event.preventDefault();
+			switch ( event.pointerType ) {
 
-		switch ( event.pointerType ) {
+				case 'mouse':
+				case 'pen':
+					onMouseDown( event );
+					break;
 
-			case 'mouse':
-			case 'pen':
-				onMouseDown( event );
-				break;
+				// TODO touch
 
-			// TODO touch
+			}
 
 		}
 
-	}
+		function onMouseDown( event ) {
 
-	function onMouseDown( event ) {
+			event.preventDefault();
 
-		event.preventDefault();
+			_intersections.length = 0;
 
-		_intersections.length = 0;
+			_raycaster.setFromCamera( _mouse, _camera );
+			_raycaster.intersectObjects( _objects, true, _intersections );
 
-		_raycaster.setFromCamera( _mouse, _camera );
-		_raycaster.intersectObjects( _objects, true, _intersections );
+			if ( _intersections.length > 0 ) {
 
-		if ( _intersections.length > 0 ) {
+				_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
 
-			_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
+				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+					_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
+					_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-				_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
-				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+				}
 
-			}
+				_domElement.style.cursor = 'move';
 
-			_domElement.style.cursor = 'move';
+				scope.dispatchEvent( { type: 'dragstart', object: _selected } );
 
-			scope.dispatchEvent( { type: 'dragstart', object: _selected } );
+			}
 
-		}
 
+		}
 
-	}
+		function onPointerCancel( event ) {
 
-	function onPointerCancel( event ) {
+			event.preventDefault();
 
-		event.preventDefault();
+			switch ( event.pointerType ) {
 
-		switch ( event.pointerType ) {
+				case 'mouse':
+				case 'pen':
+					onMouseCancel( event );
+					break;
 
-			case 'mouse':
-			case 'pen':
-				onMouseCancel( event );
-				break;
+				// TODO touch
 
-			// TODO touch
+			}
 
 		}
 
-	}
+		function onMouseCancel( event ) {
+
+			event.preventDefault();
 
-	function onMouseCancel( event ) {
+			if ( _selected ) {
 
-		event.preventDefault();
+				scope.dispatchEvent( { type: 'dragend', object: _selected } );
 
-		if ( _selected ) {
+				_selected = null;
 
-			scope.dispatchEvent( { type: 'dragend', object: _selected } );
+			}
 
-			_selected = null;
+			_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
 
 		}
 
-		_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
+		function onTouchMove( event ) {
 
-	}
+			event.preventDefault();
+			event = event.changedTouches[ 0 ];
+
+			const rect = _domElement.getBoundingClientRect();
 
-	function onTouchMove( event ) {
+			_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
-		event.preventDefault();
-		event = event.changedTouches[ 0 ];
+			_raycaster.setFromCamera( _mouse, _camera );
 
-		var rect = _domElement.getBoundingClientRect();
+			if ( _selected && scope.enabled ) {
 
-		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
-		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-		_raycaster.setFromCamera( _mouse, _camera );
+					_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
 
-		if ( _selected && scope.enabled ) {
+				}
 
-			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+				scope.dispatchEvent( { type: 'drag', object: _selected } );
 
-				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+				return;
 
 			}
 
-			scope.dispatchEvent( { type: 'drag', object: _selected } );
+		}
 
-			return;
+		function onTouchStart( event ) {
 
-		}
+			event.preventDefault();
+			event = event.changedTouches[ 0 ];
 
-	}
+			const rect = _domElement.getBoundingClientRect();
 
-	function onTouchStart( event ) {
+			_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+			_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
-		event.preventDefault();
-		event = event.changedTouches[ 0 ];
+			_intersections.length = 0;
 
-		var rect = _domElement.getBoundingClientRect();
+			_raycaster.setFromCamera( _mouse, _camera );
+			 _raycaster.intersectObjects( _objects, true, _intersections );
 
-		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
-		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+			if ( _intersections.length > 0 ) {
 
-		_intersections.length = 0;
+				_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
 
-		_raycaster.setFromCamera( _mouse, _camera );
-		 _raycaster.intersectObjects( _objects, true, _intersections );
+				_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-		if ( _intersections.length > 0 ) {
+				if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
-			_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
+					_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
+					_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 
-			_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+				}
 
-			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+				_domElement.style.cursor = 'move';
 
-				_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
-				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+				scope.dispatchEvent( { type: 'dragstart', object: _selected } );
 
 			}
 
-			_domElement.style.cursor = 'move';
-
-			scope.dispatchEvent( { type: 'dragstart', object: _selected } );
 
 		}
 
+		function onTouchEnd( event ) {
 
-	}
+			event.preventDefault();
 
-	function onTouchEnd( event ) {
+			if ( _selected ) {
 
-		event.preventDefault();
+				scope.dispatchEvent( { type: 'dragend', object: _selected } );
 
-		if ( _selected ) {
+				_selected = null;
 
-			scope.dispatchEvent( { type: 'dragend', object: _selected } );
+			}
 
-			_selected = null;
+			_domElement.style.cursor = 'auto';
 
 		}
 
-		_domElement.style.cursor = 'auto';
-
-	}
+		activate();
 
-	activate();
+		// API
 
-	// API
+		this.enabled = true;
+		this.transformGroup = false;
 
-	this.enabled = true;
-	this.transformGroup = false;
+		this.activate = activate;
+		this.deactivate = deactivate;
+		this.dispose = dispose;
+		this.getObjects = getObjects;
 
-	this.activate = activate;
-	this.deactivate = deactivate;
-	this.dispose = dispose;
-	this.getObjects = getObjects;
-
-};
+	}
 
-DragControls.prototype = Object.create( EventDispatcher.prototype );
-DragControls.prototype.constructor = DragControls;
+}
 
 export { DragControls };

+ 189 - 203
examples/jsm/controls/FirstPersonControls.js

@@ -4,349 +4,335 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-var FirstPersonControls = function ( object, domElement ) {
+const _lookDirection = new Vector3();
+const _spherical = new Spherical();
+const _target = new Vector3();
 
-	if ( domElement === undefined ) {
+class FirstPersonControls {
 
-		console.warn( 'THREE.FirstPersonControls: The second parameter "domElement" is now mandatory.' );
-		domElement = document;
+	constructor( object, domElement ) {
 
-	}
-
-	this.object = object;
-	this.domElement = domElement;
+		if ( domElement === undefined ) {
 
-	// API
+			console.warn( 'THREE.FirstPersonControls: The second parameter "domElement" is now mandatory.' );
+			domElement = document;
 
-	this.enabled = true;
-
-	this.movementSpeed = 1.0;
-	this.lookSpeed = 0.005;
+		}
 
-	this.lookVertical = true;
-	this.autoForward = false;
+		this.object = object;
+		this.domElement = domElement;
 
-	this.activeLook = true;
+		// API
 
-	this.heightSpeed = false;
-	this.heightCoef = 1.0;
-	this.heightMin = 0.0;
-	this.heightMax = 1.0;
+		this.enabled = true;
 
-	this.constrainVertical = false;
-	this.verticalMin = 0;
-	this.verticalMax = Math.PI;
+		this.movementSpeed = 1.0;
+		this.lookSpeed = 0.005;
 
-	this.mouseDragOn = false;
+		this.lookVertical = true;
+		this.autoForward = false;
 
-	// internals
+		this.activeLook = true;
 
-	this.autoSpeedFactor = 0.0;
+		this.heightSpeed = false;
+		this.heightCoef = 1.0;
+		this.heightMin = 0.0;
+		this.heightMax = 1.0;
 
-	this.mouseX = 0;
-	this.mouseY = 0;
+		this.constrainVertical = false;
+		this.verticalMin = 0;
+		this.verticalMax = Math.PI;
 
-	this.moveForward = false;
-	this.moveBackward = false;
-	this.moveLeft = false;
-	this.moveRight = false;
+		this.mouseDragOn = false;
 
-	this.viewHalfX = 0;
-	this.viewHalfY = 0;
+		// internals
 
-	// private variables
+		this.autoSpeedFactor = 0.0;
 
-	var lat = 0;
-	var lon = 0;
+		this.mouseX = 0;
+		this.mouseY = 0;
 
-	var lookDirection = new Vector3();
-	var spherical = new Spherical();
-	var target = new Vector3();
+		this.moveForward = false;
+		this.moveBackward = false;
+		this.moveLeft = false;
+		this.moveRight = false;
 
-	//
+		this.viewHalfX = 0;
+		this.viewHalfY = 0;
 
-	if ( this.domElement !== document ) {
+		// private variables
 
-		this.domElement.setAttribute( 'tabindex', - 1 );
+		let lat = 0;
+		let lon = 0;
 
-	}
+		//
 
-	//
+		this.handleResize = function () {
 
-	this.handleResize = function () {
+			if ( this.domElement === document ) {
 
-		if ( this.domElement === document ) {
+				this.viewHalfX = window.innerWidth / 2;
+				this.viewHalfY = window.innerHeight / 2;
 
-			this.viewHalfX = window.innerWidth / 2;
-			this.viewHalfY = window.innerHeight / 2;
+			} else {
 
-		} else {
+				this.viewHalfX = this.domElement.offsetWidth / 2;
+				this.viewHalfY = this.domElement.offsetHeight / 2;
 
-			this.viewHalfX = this.domElement.offsetWidth / 2;
-			this.viewHalfY = this.domElement.offsetHeight / 2;
+			}
 
-		}
+		};
 
-	};
+		this.onMouseDown = function ( event ) {
 
-	this.onMouseDown = function ( event ) {
+			if ( this.domElement !== document ) {
 
-		if ( this.domElement !== document ) {
+				this.domElement.focus();
 
-			this.domElement.focus();
+			}
 
-		}
+			event.preventDefault();
 
-		event.preventDefault();
+			if ( this.activeLook ) {
 
-		if ( this.activeLook ) {
+				switch ( event.button ) {
 
-			switch ( event.button ) {
+					case 0: this.moveForward = true; break;
+					case 2: this.moveBackward = true; break;
 
-				case 0: this.moveForward = true; break;
-				case 2: this.moveBackward = true; break;
+				}
 
 			}
 
-		}
+			this.mouseDragOn = true;
 
-		this.mouseDragOn = true;
+		};
 
-	};
+		this.onMouseUp = function ( event ) {
 
-	this.onMouseUp = function ( event ) {
+			event.preventDefault();
 
-		event.preventDefault();
+			if ( this.activeLook ) {
 
-		if ( this.activeLook ) {
+				switch ( event.button ) {
 
-			switch ( event.button ) {
+					case 0: this.moveForward = false; break;
+					case 2: this.moveBackward = false; break;
 
-				case 0: this.moveForward = false; break;
-				case 2: this.moveBackward = false; break;
+				}
 
 			}
 
-		}
-
-		this.mouseDragOn = false;
+			this.mouseDragOn = false;
 
-	};
+		};
 
-	this.onMouseMove = function ( event ) {
+		this.onMouseMove = function ( event ) {
 
-		if ( this.domElement === document ) {
+			if ( this.domElement === document ) {
 
-			this.mouseX = event.pageX - this.viewHalfX;
-			this.mouseY = event.pageY - this.viewHalfY;
+				this.mouseX = event.pageX - this.viewHalfX;
+				this.mouseY = event.pageY - this.viewHalfY;
 
-		} else {
+			} else {
 
-			this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
-			this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
+				this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
+				this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
 
-		}
+			}
 
-	};
+		};
 
-	this.onKeyDown = function ( event ) {
+		this.onKeyDown = function ( event ) {
 
-		//event.preventDefault();
+			//event.preventDefault();
 
-		switch ( event.code ) {
+			switch ( event.code ) {
 
-			case 'ArrowUp':
-			case 'KeyW': this.moveForward = true; break;
+				case 'ArrowUp':
+				case 'KeyW': this.moveForward = true; break;
 
-			case 'ArrowLeft':
-			case 'KeyA': this.moveLeft = true; break;
+				case 'ArrowLeft':
+				case 'KeyA': this.moveLeft = true; break;
 
-			case 'ArrowDown':
-			case 'KeyS': this.moveBackward = true; break;
+				case 'ArrowDown':
+				case 'KeyS': this.moveBackward = true; break;
 
-			case 'ArrowRight':
-			case 'KeyD': this.moveRight = true; break;
+				case 'ArrowRight':
+				case 'KeyD': this.moveRight = true; break;
 
-			case 'KeyR': this.moveUp = true; break;
-			case 'KeyF': this.moveDown = true; break;
+				case 'KeyR': this.moveUp = true; break;
+				case 'KeyF': this.moveDown = true; break;
 
-		}
+			}
 
-	};
+		};
 
-	this.onKeyUp = function ( event ) {
+		this.onKeyUp = function ( event ) {
 
-		switch ( event.code ) {
+			switch ( event.code ) {
 
-			case 'ArrowUp':
-			case 'KeyW': this.moveForward = false; break;
+				case 'ArrowUp':
+				case 'KeyW': this.moveForward = false; break;
 
-			case 'ArrowLeft':
-			case 'KeyA': this.moveLeft = false; break;
+				case 'ArrowLeft':
+				case 'KeyA': this.moveLeft = false; break;
 
-			case 'ArrowDown':
-			case 'KeyS': this.moveBackward = false; break;
+				case 'ArrowDown':
+				case 'KeyS': this.moveBackward = false; break;
 
-			case 'ArrowRight':
-			case 'KeyD': this.moveRight = false; break;
+				case 'ArrowRight':
+				case 'KeyD': this.moveRight = false; break;
 
-			case 'KeyR': this.moveUp = false; break;
-			case 'KeyF': this.moveDown = false; break;
+				case 'KeyR': this.moveUp = false; break;
+				case 'KeyF': this.moveDown = false; break;
 
-		}
+			}
 
-	};
+		};
 
-	this.lookAt = function ( x, y, z ) {
+		this.lookAt = function ( x, y, z ) {
 
-		if ( x.isVector3 ) {
+			if ( x.isVector3 ) {
 
-			target.copy( x );
+				_target.copy( x );
 
-		} else {
+			} else {
 
-			target.set( x, y, z );
+				_target.set( x, y, z );
 
-		}
+			}
 
-		this.object.lookAt( target );
+			this.object.lookAt( _target );
 
-		setOrientation( this );
+			setOrientation( this );
 
-		return this;
+			return this;
 
-	};
+		};
 
-	this.update = function () {
+		this.update = function () {
 
-		var targetPosition = new Vector3();
+			const targetPosition = new Vector3();
 
-		return function update( delta ) {
+			return function update( delta ) {
 
-			if ( this.enabled === false ) return;
+				if ( this.enabled === false ) return;
 
-			if ( this.heightSpeed ) {
+				if ( this.heightSpeed ) {
 
-				var y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
-				var heightDelta = y - this.heightMin;
+					const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
+					const heightDelta = y - this.heightMin;
 
-				this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
+					this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
 
-			} else {
+				} else {
 
-				this.autoSpeedFactor = 0.0;
+					this.autoSpeedFactor = 0.0;
 
-			}
+				}
 
-			var actualMoveSpeed = delta * this.movementSpeed;
+				const actualMoveSpeed = delta * this.movementSpeed;
 
-			if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
-			if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
+				if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
+				if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
 
-			if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
-			if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
+				if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
+				if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
 
-			if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
-			if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
+				if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
+				if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
 
-			var actualLookSpeed = delta * this.lookSpeed;
+				let actualLookSpeed = delta * this.lookSpeed;
 
-			if ( ! this.activeLook ) {
+				if ( ! this.activeLook ) {
 
-				actualLookSpeed = 0;
+					actualLookSpeed = 0;
 
-			}
+				}
 
-			var verticalLookRatio = 1;
+				let verticalLookRatio = 1;
 
-			if ( this.constrainVertical ) {
+				if ( this.constrainVertical ) {
 
-				verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
+					verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
 
-			}
+				}
 
-			lon -= this.mouseX * actualLookSpeed;
-			if ( this.lookVertical ) lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
+				lon -= this.mouseX * actualLookSpeed;
+				if ( this.lookVertical ) lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
 
-			lat = Math.max( - 85, Math.min( 85, lat ) );
+				lat = Math.max( - 85, Math.min( 85, lat ) );
 
-			var phi = MathUtils.degToRad( 90 - lat );
-			var theta = MathUtils.degToRad( lon );
+				let phi = MathUtils.degToRad( 90 - lat );
+				const theta = MathUtils.degToRad( lon );
 
-			if ( this.constrainVertical ) {
+				if ( this.constrainVertical ) {
 
-				phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
+					phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
 
-			}
+				}
 
-			var position = this.object.position;
+				const position = this.object.position;
 
-			targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
+				targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
 
-			this.object.lookAt( targetPosition );
+				this.object.lookAt( targetPosition );
 
-		};
+			};
 
-	}();
+		}();
 
-	function contextmenu( event ) {
+		this.dispose = function () {
 
-		event.preventDefault();
+			this.domElement.removeEventListener( 'contextmenu', contextmenu );
+			this.domElement.removeEventListener( 'mousedown', _onMouseDown );
+			this.domElement.removeEventListener( 'mousemove', _onMouseMove );
+			this.domElement.removeEventListener( 'mouseup', _onMouseUp );
 
-	}
+			window.removeEventListener( 'keydown', _onKeyDown );
+			window.removeEventListener( 'keyup', _onKeyUp );
 
-	this.dispose = function () {
+		};
 
-		this.domElement.removeEventListener( 'contextmenu', contextmenu );
-		this.domElement.removeEventListener( 'mousedown', _onMouseDown );
-		this.domElement.removeEventListener( 'mousemove', _onMouseMove );
-		this.domElement.removeEventListener( 'mouseup', _onMouseUp );
+		const _onMouseMove = this.onMouseMove.bind( this );
+		const _onMouseDown = this.onMouseDown.bind( this );
+		const _onMouseUp = this.onMouseUp.bind( this );
+		const _onKeyDown = this.onKeyDown.bind( this );
+		const _onKeyUp = this.onKeyUp.bind( this );
 
-		window.removeEventListener( 'keydown', _onKeyDown );
-		window.removeEventListener( 'keyup', _onKeyUp );
+		this.domElement.addEventListener( 'contextmenu', contextmenu );
+		this.domElement.addEventListener( 'mousemove', _onMouseMove );
+		this.domElement.addEventListener( 'mousedown', _onMouseDown );
+		this.domElement.addEventListener( 'mouseup', _onMouseUp );
 
-	};
+		window.addEventListener( 'keydown', _onKeyDown );
+		window.addEventListener( 'keyup', _onKeyUp );
 
-	var _onMouseMove = bind( this, this.onMouseMove );
-	var _onMouseDown = bind( this, this.onMouseDown );
-	var _onMouseUp = bind( this, this.onMouseUp );
-	var _onKeyDown = bind( this, this.onKeyDown );
-	var _onKeyUp = bind( this, this.onKeyUp );
+		function setOrientation( controls ) {
 
-	this.domElement.addEventListener( 'contextmenu', contextmenu );
-	this.domElement.addEventListener( 'mousemove', _onMouseMove );
-	this.domElement.addEventListener( 'mousedown', _onMouseDown );
-	this.domElement.addEventListener( 'mouseup', _onMouseUp );
+			const quaternion = controls.object.quaternion;
 
-	window.addEventListener( 'keydown', _onKeyDown );
-	window.addEventListener( 'keyup', _onKeyUp );
+			_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+			_spherical.setFromVector3( _lookDirection );
 
-	function bind( scope, fn ) {
+			lat = 90 - MathUtils.radToDeg( _spherical.phi );
+			lon = MathUtils.radToDeg( _spherical.theta );
 
-		return function () {
+		}
 
-			fn.apply( scope, arguments );
+		this.handleResize();
 
-		};
+		setOrientation( this );
 
 	}
 
-	function setOrientation( controls ) {
-
-		var quaternion = controls.object.quaternion;
-
-		lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
-		spherical.setFromVector3( lookDirection );
-
-		lat = 90 - MathUtils.radToDeg( spherical.phi );
-		lon = MathUtils.radToDeg( spherical.theta );
-
-	}
+}
 
-	this.handleResize();
+function contextmenu( event ) {
 
-	setOrientation( this );
+	event.preventDefault();
 
-};
+}
 
 export { FirstPersonControls };

+ 166 - 177
examples/jsm/controls/FlyControls.js

@@ -4,198 +4,200 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-var FlyControls = function ( object, domElement ) {
+const _changeEvent = { type: 'change' };
 
-	if ( domElement === undefined ) {
+class FlyControls extends EventDispatcher {
 
-		console.warn( 'THREE.FlyControls: The second parameter "domElement" is now mandatory.' );
-		domElement = document;
+	constructor( object, domElement ) {
 
-	}
+		super();
 
-	this.object = object;
-	this.domElement = domElement;
+		if ( domElement === undefined ) {
 
-	if ( domElement ) this.domElement.setAttribute( 'tabindex', - 1 );
+			console.warn( 'THREE.FlyControls: The second parameter "domElement" is now mandatory.' );
+			domElement = document;
 
-	// API
+		}
 
-	this.movementSpeed = 1.0;
-	this.rollSpeed = 0.005;
+		this.object = object;
+		this.domElement = domElement;
 
-	this.dragToLook = false;
-	this.autoForward = false;
+		// API
 
-	// disable default target object behavior
+		this.movementSpeed = 1.0;
+		this.rollSpeed = 0.005;
 
-	// internals
+		this.dragToLook = false;
+		this.autoForward = false;
 
-	var scope = this;
-	var changeEvent = { type: 'change' };
-	var EPS = 0.000001;
+		// disable default target object behavior
 
-	this.tmpQuaternion = new Quaternion();
+		// internals
 
-	this.mouseStatus = 0;
+		const scope = this;
 
-	this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
-	this.moveVector = new Vector3( 0, 0, 0 );
-	this.rotationVector = new Vector3( 0, 0, 0 );
+		const EPS = 0.000001;
 
-	this.keydown = function ( event ) {
+		const lastQuaternion = new Quaternion();
+		const lastPosition = new Vector3();
 
-		if ( event.altKey ) {
+		this.tmpQuaternion = new Quaternion();
 
-			return;
+		this.mouseStatus = 0;
 
-		}
+		this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
+		this.moveVector = new Vector3( 0, 0, 0 );
+		this.rotationVector = new Vector3( 0, 0, 0 );
 
-		//event.preventDefault();
+		this.keydown = function ( event ) {
 
-		switch ( event.code ) {
+			if ( event.altKey ) {
 
-			case 'ShiftLeft':
-			case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
+				return;
 
-			case 'KeyW': this.moveState.forward = 1; break;
-			case 'KeyS': this.moveState.back = 1; break;
+			}
 
-			case 'KeyA': this.moveState.left = 1; break;
-			case 'KeyD': this.moveState.right = 1; break;
+			//event.preventDefault();
 
-			case 'KeyR': this.moveState.up = 1; break;
-			case 'KeyF': this.moveState.down = 1; break;
+			switch ( event.code ) {
 
-			case 'ArrowUp': this.moveState.pitchUp = 1; break;
-			case 'ArrowDown': this.moveState.pitchDown = 1; break;
+				case 'ShiftLeft':
+				case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
 
-			case 'ArrowLeft': this.moveState.yawLeft = 1; break;
-			case 'ArrowRight': this.moveState.yawRight = 1; break;
+				case 'KeyW': this.moveState.forward = 1; break;
+				case 'KeyS': this.moveState.back = 1; break;
 
-			case 'KeyQ': this.moveState.rollLeft = 1; break;
-			case 'KeyE': this.moveState.rollRight = 1; break;
+				case 'KeyA': this.moveState.left = 1; break;
+				case 'KeyD': this.moveState.right = 1; break;
 
-		}
+				case 'KeyR': this.moveState.up = 1; break;
+				case 'KeyF': this.moveState.down = 1; break;
 
-		this.updateMovementVector();
-		this.updateRotationVector();
+				case 'ArrowUp': this.moveState.pitchUp = 1; break;
+				case 'ArrowDown': this.moveState.pitchDown = 1; break;
 
-	};
+				case 'ArrowLeft': this.moveState.yawLeft = 1; break;
+				case 'ArrowRight': this.moveState.yawRight = 1; break;
 
-	this.keyup = function ( event ) {
+				case 'KeyQ': this.moveState.rollLeft = 1; break;
+				case 'KeyE': this.moveState.rollRight = 1; break;
 
-		switch ( event.code ) {
+			}
 
-			case 'ShiftLeft':
-			case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
+			this.updateMovementVector();
+			this.updateRotationVector();
 
-			case 'KeyW': this.moveState.forward = 0; break;
-			case 'KeyS': this.moveState.back = 0; break;
+		};
 
-			case 'KeyA': this.moveState.left = 0; break;
-			case 'KeyD': this.moveState.right = 0; break;
+		this.keyup = function ( event ) {
 
-			case 'KeyR': this.moveState.up = 0; break;
-			case 'KeyF': this.moveState.down = 0; break;
+			switch ( event.code ) {
 
-			case 'ArrowUp': this.moveState.pitchUp = 0; break;
-			case 'ArrowDown': this.moveState.pitchDown = 0; break;
+				case 'ShiftLeft':
+				case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
 
-			case 'ArrowLeft': this.moveState.yawLeft = 0; break;
-			case 'ArrowRight': this.moveState.yawRight = 0; break;
+				case 'KeyW': this.moveState.forward = 0; break;
+				case 'KeyS': this.moveState.back = 0; break;
 
-			case 'KeyQ': this.moveState.rollLeft = 0; break;
-			case 'KeyE': this.moveState.rollRight = 0; break;
+				case 'KeyA': this.moveState.left = 0; break;
+				case 'KeyD': this.moveState.right = 0; break;
 
-		}
+				case 'KeyR': this.moveState.up = 0; break;
+				case 'KeyF': this.moveState.down = 0; break;
 
-		this.updateMovementVector();
-		this.updateRotationVector();
+				case 'ArrowUp': this.moveState.pitchUp = 0; break;
+				case 'ArrowDown': this.moveState.pitchDown = 0; break;
 
-	};
+				case 'ArrowLeft': this.moveState.yawLeft = 0; break;
+				case 'ArrowRight': this.moveState.yawRight = 0; break;
 
-	this.mousedown = function ( event ) {
+				case 'KeyQ': this.moveState.rollLeft = 0; break;
+				case 'KeyE': this.moveState.rollRight = 0; break;
 
-		if ( this.domElement !== document ) {
+			}
 
-			this.domElement.focus();
+			this.updateMovementVector();
+			this.updateRotationVector();
 
-		}
+		};
 
-		event.preventDefault();
+		this.mousedown = function ( event ) {
 
-		if ( this.dragToLook ) {
+			if ( this.domElement !== document ) {
 
-			this.mouseStatus ++;
+				this.domElement.focus();
 
-		} else {
+			}
 
-			switch ( event.button ) {
+			event.preventDefault();
 
-				case 0: this.moveState.forward = 1; break;
-				case 2: this.moveState.back = 1; break;
+			if ( this.dragToLook ) {
 
-			}
+				this.mouseStatus ++;
 
-			this.updateMovementVector();
+			} else {
 
-		}
+				switch ( event.button ) {
 
-	};
+					case 0: this.moveState.forward = 1; break;
+					case 2: this.moveState.back = 1; break;
 
-	this.mousemove = function ( event ) {
+				}
 
-		if ( ! this.dragToLook || this.mouseStatus > 0 ) {
+				this.updateMovementVector();
 
-			var container = this.getContainerDimensions();
-			var halfWidth = container.size[ 0 ] / 2;
-			var halfHeight = container.size[ 1 ] / 2;
+			}
 
-			this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
-			this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
+		};
 
-			this.updateRotationVector();
+		this.mousemove = function ( event ) {
 
-		}
+			if ( ! this.dragToLook || this.mouseStatus > 0 ) {
 
-	};
+				const container = this.getContainerDimensions();
+				const halfWidth = container.size[ 0 ] / 2;
+				const halfHeight = container.size[ 1 ] / 2;
 
-	this.mouseup = function ( event ) {
+				this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
+				this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
 
-		event.preventDefault();
+				this.updateRotationVector();
 
-		if ( this.dragToLook ) {
+			}
 
-			this.mouseStatus --;
+		};
 
-			this.moveState.yawLeft = this.moveState.pitchDown = 0;
+		this.mouseup = function ( event ) {
 
-		} else {
+			event.preventDefault();
 
-			switch ( event.button ) {
+			if ( this.dragToLook ) {
 
-				case 0: this.moveState.forward = 0; break;
-				case 2: this.moveState.back = 0; break;
+				this.mouseStatus --;
 
-			}
+				this.moveState.yawLeft = this.moveState.pitchDown = 0;
 
-			this.updateMovementVector();
+			} else {
 
-		}
+				switch ( event.button ) {
 
-		this.updateRotationVector();
+					case 0: this.moveState.forward = 0; break;
+					case 2: this.moveState.back = 0; break;
+
+				}
 
-	};
+				this.updateMovementVector();
 
-	this.update = function () {
+			}
+
+			this.updateRotationVector();
 
-		var lastQuaternion = new Quaternion();
-		var lastPosition = new Vector3();
+		};
 
-		return function ( delta ) {
+		this.update = function ( delta ) {
 
-			var moveMult = delta * scope.movementSpeed;
-			var rotMult = delta * scope.rollSpeed;
+			const moveMult = delta * scope.movementSpeed;
+			const rotMult = delta * scope.rollSpeed;
 
 			scope.object.translateX( scope.moveVector.x * moveMult );
 			scope.object.translateY( scope.moveVector.y * moveMult );
@@ -209,7 +211,7 @@ var FlyControls = function ( object, domElement ) {
 				8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS
 			) {
 
-				scope.dispatchEvent( changeEvent );
+				scope.dispatchEvent( _changeEvent );
 				lastQuaternion.copy( scope.object.quaternion );
 				lastPosition.copy( scope.object.position );
 
@@ -217,99 +219,86 @@ var FlyControls = function ( object, domElement ) {
 
 		};
 
-	}();
-
-	this.updateMovementVector = function () {
-
-		var forward = ( this.moveState.forward || ( this.autoForward && ! this.moveState.back ) ) ? 1 : 0;
-
-		this.moveVector.x = ( - this.moveState.left + this.moveState.right );
-		this.moveVector.y = ( - this.moveState.down + this.moveState.up );
-		this.moveVector.z = ( - forward + this.moveState.back );
+		this.updateMovementVector = function () {
 
-		//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
+			const forward = ( this.moveState.forward || ( this.autoForward && ! this.moveState.back ) ) ? 1 : 0;
 
-	};
+			this.moveVector.x = ( - this.moveState.left + this.moveState.right );
+			this.moveVector.y = ( - this.moveState.down + this.moveState.up );
+			this.moveVector.z = ( - forward + this.moveState.back );
 
-	this.updateRotationVector = function () {
+			//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
 
-		this.rotationVector.x = ( - this.moveState.pitchDown + this.moveState.pitchUp );
-		this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft );
-		this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft );
-
-		//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
+		};
 
-	};
+		this.updateRotationVector = function () {
 
-	this.getContainerDimensions = function () {
+			this.rotationVector.x = ( - this.moveState.pitchDown + this.moveState.pitchUp );
+			this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft );
+			this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft );
 
-		if ( this.domElement != document ) {
+			//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
 
-			return {
-				size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
-				offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
-			};
+		};
 
-		} else {
+		this.getContainerDimensions = function () {
 
-			return {
-				size: [ window.innerWidth, window.innerHeight ],
-				offset: [ 0, 0 ]
-			};
+			if ( this.domElement != document ) {
 
-		}
+				return {
+					size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
+					offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
+				};
 
-	};
+			} else {
 
-	function bind( scope, fn ) {
+				return {
+					size: [ window.innerWidth, window.innerHeight ],
+					offset: [ 0, 0 ]
+				};
 
-		return function () {
-
-			fn.apply( scope, arguments );
+			}
 
 		};
 
-	}
+		this.dispose = function () {
 
-	function contextmenu( event ) {
+			this.domElement.removeEventListener( 'contextmenu', contextmenu );
+			this.domElement.removeEventListener( 'mousedown', _mousedown );
+			this.domElement.removeEventListener( 'mousemove', _mousemove );
+			this.domElement.removeEventListener( 'mouseup', _mouseup );
 
-		event.preventDefault();
+			window.removeEventListener( 'keydown', _keydown );
+			window.removeEventListener( 'keyup', _keyup );
 
-	}
-
-	this.dispose = function () {
+		};
 
-		this.domElement.removeEventListener( 'contextmenu', contextmenu );
-		this.domElement.removeEventListener( 'mousedown', _mousedown );
-		this.domElement.removeEventListener( 'mousemove', _mousemove );
-		this.domElement.removeEventListener( 'mouseup', _mouseup );
+		const _mousemove = this.mousemove.bind( this );
+		const _mousedown = this.mousedown.bind( this );
+		const _mouseup = this.mouseup.bind( this );
+		const _keydown = this.keydown.bind( this );
+		const _keyup = this.keyup.bind( this );
 
-		window.removeEventListener( 'keydown', _keydown );
-		window.removeEventListener( 'keyup', _keyup );
+		this.domElement.addEventListener( 'contextmenu', contextmenu );
 
-	};
+		this.domElement.addEventListener( 'mousemove', _mousemove );
+		this.domElement.addEventListener( 'mousedown', _mousedown );
+		this.domElement.addEventListener( 'mouseup', _mouseup );
 
-	var _mousemove = bind( this, this.mousemove );
-	var _mousedown = bind( this, this.mousedown );
-	var _mouseup = bind( this, this.mouseup );
-	var _keydown = bind( this, this.keydown );
-	var _keyup = bind( this, this.keyup );
+		window.addEventListener( 'keydown', _keydown );
+		window.addEventListener( 'keyup', _keyup );
 
-	this.domElement.addEventListener( 'contextmenu', contextmenu );
+		this.updateMovementVector();
+		this.updateRotationVector();
 
-	this.domElement.addEventListener( 'mousemove', _mousemove );
-	this.domElement.addEventListener( 'mousedown', _mousedown );
-	this.domElement.addEventListener( 'mouseup', _mouseup );
+	}
 
-	window.addEventListener( 'keydown', _keydown );
-	window.addEventListener( 'keyup', _keyup );
+}
 
-	this.updateMovementVector();
-	this.updateRotationVector();
+function contextmenu( event ) {
 
-};
+	event.preventDefault();
 
-FlyControls.prototype = Object.create( EventDispatcher.prototype );
-FlyControls.prototype.constructor = FlyControls;
+}
 
 export { FlyControls };

File diff ditekan karena terlalu besar
+ 659 - 656
examples/jsm/controls/OrbitControls.js


+ 85 - 87
examples/jsm/controls/PointerLockControls.js

@@ -4,161 +4,159 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-var PointerLockControls = function ( camera, domElement ) {
+const _euler = new Euler( 0, 0, 0, 'YXZ' );
+const _vector = new Vector3();
 
-	if ( domElement === undefined ) {
+const _changeEvent = { type: 'change' };
+const _lockEvent = { type: 'lock' };
+const _unlockEvent = { type: 'unlock' };
 
-		console.warn( 'THREE.PointerLockControls: The second parameter "domElement" is now mandatory.' );
-		domElement = document.body;
+const _PI_2 = Math.PI / 2;
 
-	}
+class PointerLockControls extends EventDispatcher {
 
-	this.domElement = domElement;
-	this.isLocked = false;
+	constructor( camera, domElement ) {
 
-	// Set to constrain the pitch of the camera
-	// Range is 0 to Math.PI radians
-	this.minPolarAngle = 0; // radians
-	this.maxPolarAngle = Math.PI; // radians
+		super();
 
-	//
-	// internals
-	//
+		if ( domElement === undefined ) {
 
-	var scope = this;
+			console.warn( 'THREE.PointerLockControls: The second parameter "domElement" is now mandatory.' );
+			domElement = document.body;
 
-	var changeEvent = { type: 'change' };
-	var lockEvent = { type: 'lock' };
-	var unlockEvent = { type: 'unlock' };
+		}
 
-	var euler = new Euler( 0, 0, 0, 'YXZ' );
+		this.domElement = domElement;
+		this.isLocked = false;
 
-	var PI_2 = Math.PI / 2;
+		// Set to constrain the pitch of the camera
+		// Range is 0 to Math.PI radians
+		this.minPolarAngle = 0; // radians
+		this.maxPolarAngle = Math.PI; // radians
 
-	var vec = new Vector3();
+		const scope = this;
 
-	function onMouseMove( event ) {
+		function onMouseMove( event ) {
 
-		if ( scope.isLocked === false ) return;
+			if ( scope.isLocked === false ) return;
 
-		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
-		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
+			const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
+			const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
 
-		euler.setFromQuaternion( camera.quaternion );
+			_euler.setFromQuaternion( camera.quaternion );
 
-		euler.y -= movementX * 0.002;
-		euler.x -= movementY * 0.002;
+			_euler.y -= movementX * 0.002;
+			_euler.x -= movementY * 0.002;
 
-		euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );
+			_euler.x = Math.max( _PI_2 - scope.maxPolarAngle, Math.min( _PI_2 - scope.minPolarAngle, _euler.x ) );
 
-		camera.quaternion.setFromEuler( euler );
+			camera.quaternion.setFromEuler( _euler );
 
-		scope.dispatchEvent( changeEvent );
+			scope.dispatchEvent( _changeEvent );
 
-	}
+		}
 
-	function onPointerlockChange() {
+		function onPointerlockChange() {
 
-		if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
+			if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
 
-			scope.dispatchEvent( lockEvent );
+				scope.dispatchEvent( _lockEvent );
 
-			scope.isLocked = true;
+				scope.isLocked = true;
 
-		} else {
+			} else {
 
-			scope.dispatchEvent( unlockEvent );
+				scope.dispatchEvent( _unlockEvent );
 
-			scope.isLocked = false;
+				scope.isLocked = false;
 
-		}
+			}
 
-	}
+		}
 
-	function onPointerlockError() {
+		function onPointerlockError() {
 
-		console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
+			console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
 
-	}
+		}
 
-	this.connect = function () {
+		this.connect = function () {
 
-		scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
-		scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange );
-		scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError );
+			scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
+			scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange );
+			scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError );
 
-	};
+		};
 
-	this.disconnect = function () {
+		this.disconnect = function () {
 
-		scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError );
+			scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
+			scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange );
+			scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError );
 
-	};
+		};
 
-	this.dispose = function () {
+		this.dispose = function () {
 
-		this.disconnect();
+			this.disconnect();
 
-	};
+		};
 
-	this.getObject = function () { // retaining this method for backward compatibility
+		this.getObject = function () { // retaining this method for backward compatibility
 
-		return camera;
+			return camera;
 
-	};
+		};
 
-	this.getDirection = function () {
+		this.getDirection = function () {
 
-		var direction = new Vector3( 0, 0, - 1 );
+			const direction = new Vector3( 0, 0, - 1 );
 
-		return function ( v ) {
+			return function ( v ) {
 
-			return v.copy( direction ).applyQuaternion( camera.quaternion );
+				return v.copy( direction ).applyQuaternion( camera.quaternion );
 
-		};
+			};
 
-	}();
+		}();
 
-	this.moveForward = function ( distance ) {
+		this.moveForward = function ( distance ) {
 
-		// move forward parallel to the xz-plane
-		// assumes camera.up is y-up
+			// move forward parallel to the xz-plane
+			// assumes camera.up is y-up
 
-		vec.setFromMatrixColumn( camera.matrix, 0 );
+			_vector.setFromMatrixColumn( camera.matrix, 0 );
 
-		vec.crossVectors( camera.up, vec );
+			_vector.crossVectors( camera.up, _vector );
 
-		camera.position.addScaledVector( vec, distance );
+			camera.position.addScaledVector( _vector, distance );
 
-	};
+		};
 
-	this.moveRight = function ( distance ) {
+		this.moveRight = function ( distance ) {
 
-		vec.setFromMatrixColumn( camera.matrix, 0 );
+			_vector.setFromMatrixColumn( camera.matrix, 0 );
 
-		camera.position.addScaledVector( vec, distance );
+			camera.position.addScaledVector( _vector, distance );
 
-	};
+		};
 
-	this.lock = function () {
+		this.lock = function () {
 
-		this.domElement.requestPointerLock();
+			this.domElement.requestPointerLock();
 
-	};
+		};
 
-	this.unlock = function () {
+		this.unlock = function () {
 
-		scope.domElement.ownerDocument.exitPointerLock();
+			scope.domElement.ownerDocument.exitPointerLock();
 
-	};
+		};
 
-	this.connect();
+		this.connect();
 
-};
+	}
 
-PointerLockControls.prototype = Object.create( EventDispatcher.prototype );
-PointerLockControls.prototype.constructor = PointerLockControls;
+}
 
 export { PointerLockControls };

+ 427 - 427
examples/jsm/controls/TrackballControls.js

@@ -6,744 +6,744 @@ import {
 	Vector3
 } from '../../../build/three.module.js';
 
-var TrackballControls = function ( object, domElement ) {
+const _changeEvent = { type: 'change' };
+const _startEvent = { type: 'start' };
+const _endEvent = { type: 'end' };
 
-	if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
-	if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
+class TrackballControls extends EventDispatcher {
 
-	var scope = this;
-	var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
+	constructor( object, domElement ) {
 
-	this.object = object;
-	this.domElement = domElement;
+		super();
 
-	// API
+		if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
+		if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
 
-	this.enabled = true;
+		const scope = this;
+		const STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
 
-	this.screen = { left: 0, top: 0, width: 0, height: 0 };
+		this.object = object;
+		this.domElement = domElement;
 
-	this.rotateSpeed = 1.0;
-	this.zoomSpeed = 1.2;
-	this.panSpeed = 0.3;
+		// API
 
-	this.noRotate = false;
-	this.noZoom = false;
-	this.noPan = false;
+		this.enabled = true;
 
-	this.staticMoving = false;
-	this.dynamicDampingFactor = 0.2;
+		this.screen = { left: 0, top: 0, width: 0, height: 0 };
 
-	this.minDistance = 0;
-	this.maxDistance = Infinity;
+		this.rotateSpeed = 1.0;
+		this.zoomSpeed = 1.2;
+		this.panSpeed = 0.3;
 
-	this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
+		this.noRotate = false;
+		this.noZoom = false;
+		this.noPan = false;
 
-	this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
+		this.staticMoving = false;
+		this.dynamicDampingFactor = 0.2;
 
-	// internals
+		this.minDistance = 0;
+		this.maxDistance = Infinity;
 
-	this.target = new Vector3();
+		this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
 
-	var EPS = 0.000001;
+		this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
 
-	var lastPosition = new Vector3();
-	var lastZoom = 1;
+		// internals
 
-	var _state = STATE.NONE,
-		_keyState = STATE.NONE,
+		this.target = new Vector3();
 
-		_eye = new Vector3(),
+		const EPS = 0.000001;
 
-		_movePrev = new Vector2(),
-		_moveCurr = new Vector2(),
+		const lastPosition = new Vector3();
+		let lastZoom = 1;
 
-		_lastAxis = new Vector3(),
-		_lastAngle = 0,
+		let _state = STATE.NONE,
+			_keyState = STATE.NONE,
 
-		_zoomStart = new Vector2(),
-		_zoomEnd = new Vector2(),
+			_touchZoomDistanceStart = 0,
+			_touchZoomDistanceEnd = 0,
 
-		_touchZoomDistanceStart = 0,
-		_touchZoomDistanceEnd = 0,
+			_lastAngle = 0;
 
-		_panStart = new Vector2(),
-		_panEnd = new Vector2();
+		const _eye = new Vector3(),
 
-	// for reset
+			_movePrev = new Vector2(),
+			_moveCurr = new Vector2(),
 
-	this.target0 = this.target.clone();
-	this.position0 = this.object.position.clone();
-	this.up0 = this.object.up.clone();
-	this.zoom0 = this.object.zoom;
+			_lastAxis = new Vector3(),
 
-	// events
+			_zoomStart = new Vector2(),
+			_zoomEnd = new Vector2(),
 
-	var changeEvent = { type: 'change' };
-	var startEvent = { type: 'start' };
-	var endEvent = { type: 'end' };
+			_panStart = new Vector2(),
+			_panEnd = new Vector2();
 
+		// for reset
 
-	// methods
+		this.target0 = this.target.clone();
+		this.position0 = this.object.position.clone();
+		this.up0 = this.object.up.clone();
+		this.zoom0 = this.object.zoom;
 
-	this.handleResize = function () {
+		// methods
 
-		var box = scope.domElement.getBoundingClientRect();
-		// adjustments come from similar code in the jquery offset() function
-		var d = scope.domElement.ownerDocument.documentElement;
-		scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
-		scope.screen.top = box.top + window.pageYOffset - d.clientTop;
-		scope.screen.width = box.width;
-		scope.screen.height = box.height;
+		this.handleResize = function () {
 
-	};
+			const box = scope.domElement.getBoundingClientRect();
+			// adjustments come from similar code in the jquery offset() function
+			const d = scope.domElement.ownerDocument.documentElement;
+			scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
+			scope.screen.top = box.top + window.pageYOffset - d.clientTop;
+			scope.screen.width = box.width;
+			scope.screen.height = box.height;
 
-	var getMouseOnScreen = ( function () {
+		};
 
-		var vector = new Vector2();
+		const getMouseOnScreen = ( function () {
 
-		return function getMouseOnScreen( pageX, pageY ) {
+			const vector = new Vector2();
 
-			vector.set(
-				( pageX - scope.screen.left ) / scope.screen.width,
-				( pageY - scope.screen.top ) / scope.screen.height
-			);
+			return function getMouseOnScreen( pageX, pageY ) {
 
-			return vector;
+				vector.set(
+					( pageX - scope.screen.left ) / scope.screen.width,
+					( pageY - scope.screen.top ) / scope.screen.height
+				);
 
-		};
+				return vector;
 
-	}() );
+			};
 
-	var getMouseOnCircle = ( function () {
+		}() );
 
-		var vector = new Vector2();
+		const getMouseOnCircle = ( function () {
 
-		return function getMouseOnCircle( pageX, pageY ) {
+			const vector = new Vector2();
 
-			vector.set(
-				( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
-				( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
-			);
+			return function getMouseOnCircle( pageX, pageY ) {
 
-			return vector;
+				vector.set(
+					( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
+					( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
+				);
 
-		};
+				return vector;
 
-	}() );
+			};
 
-	this.rotateCamera = ( function () {
+		}() );
 
-		var axis = new Vector3(),
-			quaternion = new Quaternion(),
-			eyeDirection = new Vector3(),
-			objectUpDirection = new Vector3(),
-			objectSidewaysDirection = new Vector3(),
-			moveDirection = new Vector3(),
-			angle;
+		this.rotateCamera = ( function () {
 
-		return function rotateCamera() {
+			const axis = new Vector3(),
+				quaternion = new Quaternion(),
+				eyeDirection = new Vector3(),
+				objectUpDirection = new Vector3(),
+				objectSidewaysDirection = new Vector3(),
+				moveDirection = new Vector3();
 
-			moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
-			angle = moveDirection.length();
+			return function rotateCamera() {
 
-			if ( angle ) {
+				moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
+				let angle = moveDirection.length();
 
-				_eye.copy( scope.object.position ).sub( scope.target );
+				if ( angle ) {
 
-				eyeDirection.copy( _eye ).normalize();
-				objectUpDirection.copy( scope.object.up ).normalize();
-				objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
+					_eye.copy( scope.object.position ).sub( scope.target );
 
-				objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
-				objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
+					eyeDirection.copy( _eye ).normalize();
+					objectUpDirection.copy( scope.object.up ).normalize();
+					objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
 
-				moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
+					objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
+					objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
 
-				axis.crossVectors( moveDirection, _eye ).normalize();
+					moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
 
-				angle *= scope.rotateSpeed;
-				quaternion.setFromAxisAngle( axis, angle );
+					axis.crossVectors( moveDirection, _eye ).normalize();
 
-				_eye.applyQuaternion( quaternion );
-				scope.object.up.applyQuaternion( quaternion );
+					angle *= scope.rotateSpeed;
+					quaternion.setFromAxisAngle( axis, angle );
 
-				_lastAxis.copy( axis );
-				_lastAngle = angle;
+					_eye.applyQuaternion( quaternion );
+					scope.object.up.applyQuaternion( quaternion );
 
-			} else if ( ! scope.staticMoving && _lastAngle ) {
+					_lastAxis.copy( axis );
+					_lastAngle = angle;
 
-				_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
-				_eye.copy( scope.object.position ).sub( scope.target );
-				quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
-				_eye.applyQuaternion( quaternion );
-				scope.object.up.applyQuaternion( quaternion );
+				} else if ( ! scope.staticMoving && _lastAngle ) {
 
-			}
+					_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
+					_eye.copy( scope.object.position ).sub( scope.target );
+					quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
+					_eye.applyQuaternion( quaternion );
+					scope.object.up.applyQuaternion( quaternion );
 
-			_movePrev.copy( _moveCurr );
+				}
 
-		};
+				_movePrev.copy( _moveCurr );
 
-	}() );
+			};
 
+		}() );
 
-	this.zoomCamera = function () {
 
-		var factor;
+		this.zoomCamera = function () {
 
-		if ( _state === STATE.TOUCH_ZOOM_PAN ) {
+			let factor;
 
-			factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
-			_touchZoomDistanceStart = _touchZoomDistanceEnd;
+			if ( _state === STATE.TOUCH_ZOOM_PAN ) {
 
-			if ( scope.object.isPerspectiveCamera ) {
+				factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+				_touchZoomDistanceStart = _touchZoomDistanceEnd;
 
-				_eye.multiplyScalar( factor );
+				if ( scope.object.isPerspectiveCamera ) {
 
-			} else if ( scope.object.isOrthographicCamera ) {
+					_eye.multiplyScalar( factor );
 
-				scope.object.zoom *= factor;
-				scope.object.updateProjectionMatrix();
+				} else if ( scope.object.isOrthographicCamera ) {
 
-			} else {
+					scope.object.zoom *= factor;
+					scope.object.updateProjectionMatrix();
 
-				console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+				} else {
 
-			}
+					console.warn( 'THREE.TrackballControls: Unsupported camera type' );
 
-		} else {
+				}
 
-			factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
+			} else {
 
-			if ( factor !== 1.0 && factor > 0.0 ) {
+				factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
 
-				if ( scope.object.isPerspectiveCamera ) {
+				if ( factor !== 1.0 && factor > 0.0 ) {
 
-					_eye.multiplyScalar( factor );
+					if ( scope.object.isPerspectiveCamera ) {
 
-				} else if ( scope.object.isOrthographicCamera ) {
+						_eye.multiplyScalar( factor );
 
-					scope.object.zoom /= factor;
-					scope.object.updateProjectionMatrix();
+					} else if ( scope.object.isOrthographicCamera ) {
 
-				} else {
+						scope.object.zoom /= factor;
+						scope.object.updateProjectionMatrix();
 
-					console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+					} else {
+
+						console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+
+					}
 
 				}
 
-			}
+				if ( scope.staticMoving ) {
 
-			if ( scope.staticMoving ) {
+					_zoomStart.copy( _zoomEnd );
 
-				_zoomStart.copy( _zoomEnd );
+				} else {
 
-			} else {
+					_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
 
-				_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
+				}
 
 			}
 
-		}
+		};
 
-	};
+		this.panCamera = ( function () {
 
-	this.panCamera = ( function () {
+			const mouseChange = new Vector2(),
+				objectUp = new Vector3(),
+				pan = new Vector3();
 
-		var mouseChange = new Vector2(),
-			objectUp = new Vector3(),
-			pan = new Vector3();
+			return function panCamera() {
 
-		return function panCamera() {
+				mouseChange.copy( _panEnd ).sub( _panStart );
 
-			mouseChange.copy( _panEnd ).sub( _panStart );
+				if ( mouseChange.lengthSq() ) {
 
-			if ( mouseChange.lengthSq() ) {
+					if ( scope.object.isOrthographicCamera ) {
 
-				if ( scope.object.isOrthographicCamera ) {
+						const scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
+						const scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
 
-					var scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
-					var scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
+						mouseChange.x *= scale_x;
+						mouseChange.y *= scale_y;
 
-					mouseChange.x *= scale_x;
-					mouseChange.y *= scale_y;
+					}
 
-				}
+					mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
 
-				mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
+					pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
+					pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
 
-				pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
-				pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
+					scope.object.position.add( pan );
+					scope.target.add( pan );
 
-				scope.object.position.add( pan );
-				scope.target.add( pan );
+					if ( scope.staticMoving ) {
 
-				if ( scope.staticMoving ) {
+						_panStart.copy( _panEnd );
 
-					_panStart.copy( _panEnd );
+					} else {
 
-				} else {
+						_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
 
-					_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
+					}
 
 				}
 
-			}
+			};
 
-		};
+		}() );
 
-	}() );
+		this.checkDistances = function () {
 
-	this.checkDistances = function () {
+			if ( ! scope.noZoom || ! scope.noPan ) {
 
-		if ( ! scope.noZoom || ! scope.noPan ) {
+				if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
 
-			if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
+					scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
+					_zoomStart.copy( _zoomEnd );
 
-				scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
-				_zoomStart.copy( _zoomEnd );
+				}
 
-			}
+				if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
 
-			if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
+					scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
+					_zoomStart.copy( _zoomEnd );
 
-				scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
-				_zoomStart.copy( _zoomEnd );
+				}
 
 			}
 
-		}
+		};
 
-	};
+		this.update = function () {
 
-	this.update = function () {
+			_eye.subVectors( scope.object.position, scope.target );
 
-		_eye.subVectors( scope.object.position, scope.target );
+			if ( ! scope.noRotate ) {
 
-		if ( ! scope.noRotate ) {
+				scope.rotateCamera();
 
-			scope.rotateCamera();
+			}
 
-		}
+			if ( ! scope.noZoom ) {
 
-		if ( ! scope.noZoom ) {
+				scope.zoomCamera();
 
-			scope.zoomCamera();
+			}
 
-		}
+			if ( ! scope.noPan ) {
 
-		if ( ! scope.noPan ) {
+				scope.panCamera();
 
-			scope.panCamera();
+			}
 
-		}
+			scope.object.position.addVectors( scope.target, _eye );
 
-		scope.object.position.addVectors( scope.target, _eye );
+			if ( scope.object.isPerspectiveCamera ) {
 
-		if ( scope.object.isPerspectiveCamera ) {
+				scope.checkDistances();
 
-			scope.checkDistances();
+				scope.object.lookAt( scope.target );
 
-			scope.object.lookAt( scope.target );
+				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
 
-			if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
+					scope.dispatchEvent( _changeEvent );
 
-				scope.dispatchEvent( changeEvent );
+					lastPosition.copy( scope.object.position );
 
-				lastPosition.copy( scope.object.position );
+				}
 
-			}
+			} else if ( scope.object.isOrthographicCamera ) {
 
-		} else if ( scope.object.isOrthographicCamera ) {
+				scope.object.lookAt( scope.target );
 
-			scope.object.lookAt( scope.target );
+				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
 
-			if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
+					scope.dispatchEvent( _changeEvent );
 
-				scope.dispatchEvent( changeEvent );
+					lastPosition.copy( scope.object.position );
+					lastZoom = scope.object.zoom;
 
-				lastPosition.copy( scope.object.position );
-				lastZoom = scope.object.zoom;
+				}
 
-			}
+			} else {
 
-		} else {
+				console.warn( 'THREE.TrackballControls: Unsupported camera type' );
 
-			console.warn( 'THREE.TrackballControls: Unsupported camera type' );
+			}
 
-		}
+		};
 
-	};
+		this.reset = function () {
 
-	this.reset = function () {
+			_state = STATE.NONE;
+			_keyState = STATE.NONE;
 
-		_state = STATE.NONE;
-		_keyState = STATE.NONE;
+			scope.target.copy( scope.target0 );
+			scope.object.position.copy( scope.position0 );
+			scope.object.up.copy( scope.up0 );
+			scope.object.zoom = scope.zoom0;
 
-		scope.target.copy( scope.target0 );
-		scope.object.position.copy( scope.position0 );
-		scope.object.up.copy( scope.up0 );
-		scope.object.zoom = scope.zoom0;
+			scope.object.updateProjectionMatrix();
 
-		scope.object.updateProjectionMatrix();
+			_eye.subVectors( scope.object.position, scope.target );
 
-		_eye.subVectors( scope.object.position, scope.target );
+			scope.object.lookAt( scope.target );
 
-		scope.object.lookAt( scope.target );
+			scope.dispatchEvent( _changeEvent );
 
-		scope.dispatchEvent( changeEvent );
+			lastPosition.copy( scope.object.position );
+			lastZoom = scope.object.zoom;
 
-		lastPosition.copy( scope.object.position );
-		lastZoom = scope.object.zoom;
+		};
 
-	};
+		// listeners
 
-	// listeners
+		function onPointerDown( event ) {
 
-	function onPointerDown( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			switch ( event.pointerType ) {
 
-		switch ( event.pointerType ) {
+				case 'mouse':
+				case 'pen':
+					onMouseDown( event );
+					break;
 
-			case 'mouse':
-			case 'pen':
-				onMouseDown( event );
-				break;
+				// TODO touch
 
-			// TODO touch
+			}
 
 		}
 
-	}
+		function onPointerMove( event ) {
 
-	function onPointerMove( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			switch ( event.pointerType ) {
 
-		switch ( event.pointerType ) {
+				case 'mouse':
+				case 'pen':
+					onMouseMove( event );
+					break;
 
-			case 'mouse':
-			case 'pen':
-				onMouseMove( event );
-				break;
+				// TODO touch
 
-			// TODO touch
+			}
 
 		}
 
-	}
+		function onPointerUp( event ) {
 
-	function onPointerUp( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			switch ( event.pointerType ) {
 
-		switch ( event.pointerType ) {
+				case 'mouse':
+				case 'pen':
+					onMouseUp( event );
+					break;
 
-			case 'mouse':
-			case 'pen':
-				onMouseUp( event );
-				break;
+				// TODO touch
 
-			// TODO touch
+			}
 
 		}
 
-	}
+		function keydown( event ) {
 
-	function keydown( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			window.removeEventListener( 'keydown', keydown );
 
-		window.removeEventListener( 'keydown', keydown );
+			if ( _keyState !== STATE.NONE ) {
 
-		if ( _keyState !== STATE.NONE ) {
+				return;
 
-			return;
+			} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
 
-		} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
+				_keyState = STATE.ROTATE;
 
-			_keyState = STATE.ROTATE;
+			} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
 
-		} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
+				_keyState = STATE.ZOOM;
 
-			_keyState = STATE.ZOOM;
+			} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
 
-		} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
+				_keyState = STATE.PAN;
 
-			_keyState = STATE.PAN;
+			}
 
 		}
 
-	}
+		function keyup() {
 
-	function keyup() {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			_keyState = STATE.NONE;
 
-		_keyState = STATE.NONE;
+			window.addEventListener( 'keydown', keydown );
 
-		window.addEventListener( 'keydown', keydown );
+		}
 
-	}
+		function onMouseDown( event ) {
 
-	function onMouseDown( event ) {
+			event.preventDefault();
 
-		event.preventDefault();
+			if ( _state === STATE.NONE ) {
 
-		if ( _state === STATE.NONE ) {
+				switch ( event.button ) {
 
-			switch ( event.button ) {
+					case scope.mouseButtons.LEFT:
+						_state = STATE.ROTATE;
+						break;
 
-				case scope.mouseButtons.LEFT:
-					_state = STATE.ROTATE;
-					break;
+					case scope.mouseButtons.MIDDLE:
+						_state = STATE.ZOOM;
+						break;
 
-				case scope.mouseButtons.MIDDLE:
-					_state = STATE.ZOOM;
-					break;
+					case scope.mouseButtons.RIGHT:
+						_state = STATE.PAN;
+						break;
 
-				case scope.mouseButtons.RIGHT:
-					_state = STATE.PAN;
-					break;
+					default:
+						_state = STATE.NONE;
 
-				default:
-					_state = STATE.NONE;
+				}
 
 			}
 
-		}
+			const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
 
-		var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
+			if ( state === STATE.ROTATE && ! scope.noRotate ) {
 
-		if ( state === STATE.ROTATE && ! scope.noRotate ) {
+				_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
+				_movePrev.copy( _moveCurr );
 
-			_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-			_movePrev.copy( _moveCurr );
+			} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
 
-		} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
+				_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+				_zoomEnd.copy( _zoomStart );
 
-			_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-			_zoomEnd.copy( _zoomStart );
+			} else if ( state === STATE.PAN && ! scope.noPan ) {
 
-		} else if ( state === STATE.PAN && ! scope.noPan ) {
+				_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+				_panEnd.copy( _panStart );
 
-			_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-			_panEnd.copy( _panStart );
+			}
 
-		}
+			scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
+			scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
 
-		scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
-		scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
+			scope.dispatchEvent( _startEvent );
 
-		scope.dispatchEvent( startEvent );
+		}
 
-	}
+		function onMouseMove( event ) {
 
-	function onMouseMove( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			event.preventDefault();
 
-		event.preventDefault();
+			const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
 
-		var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
+			if ( state === STATE.ROTATE && ! scope.noRotate ) {
 
-		if ( state === STATE.ROTATE && ! scope.noRotate ) {
+				_movePrev.copy( _moveCurr );
+				_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
 
-			_movePrev.copy( _moveCurr );
-			_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
+			} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
 
-		} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
+				_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-			_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+			} else if ( state === STATE.PAN && ! scope.noPan ) {
 
-		} else if ( state === STATE.PAN && ! scope.noPan ) {
+				_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
-			_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
+			}
 
 		}
 
-	}
+		function onMouseUp( event ) {
 
-	function onMouseUp( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			event.preventDefault();
 
-		event.preventDefault();
+			_state = STATE.NONE;
 
-		_state = STATE.NONE;
+			scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
+			scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
 
-		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+			scope.dispatchEvent( _endEvent );
 
-		scope.dispatchEvent( endEvent );
+		}
 
-	}
+		function mousewheel( event ) {
 
-	function mousewheel( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			if ( scope.noZoom === true ) return;
 
-		if ( scope.noZoom === true ) return;
+			event.preventDefault();
 
-		event.preventDefault();
+			switch ( event.deltaMode ) {
 
-		switch ( event.deltaMode ) {
+				case 2:
+					// Zoom in pages
+					_zoomStart.y -= event.deltaY * 0.025;
+					break;
 
-			case 2:
-				// Zoom in pages
-				_zoomStart.y -= event.deltaY * 0.025;
-				break;
+				case 1:
+					// Zoom in lines
+					_zoomStart.y -= event.deltaY * 0.01;
+					break;
+
+				default:
+					// undefined, 0, assume pixels
+					_zoomStart.y -= event.deltaY * 0.00025;
+					break;
 
-			case 1:
-				// Zoom in lines
-				_zoomStart.y -= event.deltaY * 0.01;
-				break;
+			}
 
-			default:
-				// undefined, 0, assume pixels
-				_zoomStart.y -= event.deltaY * 0.00025;
-				break;
+			scope.dispatchEvent( _startEvent );
+			scope.dispatchEvent( _endEvent );
 
 		}
 
-		scope.dispatchEvent( startEvent );
-		scope.dispatchEvent( endEvent );
+		function touchstart( event ) {
 
-	}
+			if ( scope.enabled === false ) return;
 
-	function touchstart( event ) {
+			event.preventDefault();
 
-		if ( scope.enabled === false ) return;
+			switch ( event.touches.length ) {
 
-		event.preventDefault();
+				case 1:
+					_state = STATE.TOUCH_ROTATE;
+					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+					_movePrev.copy( _moveCurr );
+					break;
 
-		switch ( event.touches.length ) {
+				default: // 2 or more
+					_state = STATE.TOUCH_ZOOM_PAN;
+					const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+					const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+					_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
 
-			case 1:
-				_state = STATE.TOUCH_ROTATE;
-				_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
-				_movePrev.copy( _moveCurr );
-				break;
+					const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+					const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+					_panStart.copy( getMouseOnScreen( x, y ) );
+					_panEnd.copy( _panStart );
+					break;
 
-			default: // 2 or more
-				_state = STATE.TOUCH_ZOOM_PAN;
-				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
-				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-				_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
+			}
 
-				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
-				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
-				_panStart.copy( getMouseOnScreen( x, y ) );
-				_panEnd.copy( _panStart );
-				break;
+			scope.dispatchEvent( _startEvent );
 
 		}
 
-		scope.dispatchEvent( startEvent );
-
-	}
+		function touchmove( event ) {
 
-	function touchmove( event ) {
+			if ( scope.enabled === false ) return;
 
-		if ( scope.enabled === false ) return;
+			event.preventDefault();
 
-		event.preventDefault();
+			switch ( event.touches.length ) {
 
-		switch ( event.touches.length ) {
+				case 1:
+					_movePrev.copy( _moveCurr );
+					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+					break;
 
-			case 1:
-				_movePrev.copy( _moveCurr );
-				_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
-				break;
+				default: // 2 or more
+					const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+					const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+					_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
 
-			default: // 2 or more
-				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
-				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
+					const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+					const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+					_panEnd.copy( getMouseOnScreen( x, y ) );
+					break;
 
-				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
-				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
-				_panEnd.copy( getMouseOnScreen( x, y ) );
-				break;
+			}
 
 		}
 
-	}
-
-	function touchend( event ) {
+		function touchend( event ) {
 
-		if ( scope.enabled === false ) return;
+			if ( scope.enabled === false ) return;
 
-		switch ( event.touches.length ) {
+			switch ( event.touches.length ) {
 
-			case 0:
-				_state = STATE.NONE;
-				break;
+				case 0:
+					_state = STATE.NONE;
+					break;
 
-			case 1:
-				_state = STATE.TOUCH_ROTATE;
-				_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
-				_movePrev.copy( _moveCurr );
-				break;
+				case 1:
+					_state = STATE.TOUCH_ROTATE;
+					_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+					_movePrev.copy( _moveCurr );
+					break;
 
-		}
+			}
 
-		scope.dispatchEvent( endEvent );
+			scope.dispatchEvent( _endEvent );
 
-	}
+		}
 
-	function contextmenu( event ) {
+		function contextmenu( event ) {
 
-		if ( scope.enabled === false ) return;
+			if ( scope.enabled === false ) return;
 
-		event.preventDefault();
+			event.preventDefault();
 
-	}
+		}
 
-	this.dispose = function () {
+		this.dispose = function () {
 
-		scope.domElement.removeEventListener( 'contextmenu', contextmenu );
+			scope.domElement.removeEventListener( 'contextmenu', contextmenu );
 
-		scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
-		scope.domElement.removeEventListener( 'wheel', mousewheel );
+			scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
+			scope.domElement.removeEventListener( 'wheel', mousewheel );
 
-		scope.domElement.removeEventListener( 'touchstart', touchstart );
-		scope.domElement.removeEventListener( 'touchend', touchend );
-		scope.domElement.removeEventListener( 'touchmove', touchmove );
+			scope.domElement.removeEventListener( 'touchstart', touchstart );
+			scope.domElement.removeEventListener( 'touchend', touchend );
+			scope.domElement.removeEventListener( 'touchmove', touchmove );
 
-		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+			scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
+			scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
 
-		window.removeEventListener( 'keydown', keydown );
-		window.removeEventListener( 'keyup', keyup );
+			window.removeEventListener( 'keydown', keydown );
+			window.removeEventListener( 'keyup', keyup );
 
-	};
+		};
 
-	this.domElement.addEventListener( 'contextmenu', contextmenu );
+		this.domElement.addEventListener( 'contextmenu', contextmenu );
 
-	this.domElement.addEventListener( 'pointerdown', onPointerDown );
-	this.domElement.addEventListener( 'wheel', mousewheel );
+		this.domElement.addEventListener( 'pointerdown', onPointerDown );
+		this.domElement.addEventListener( 'wheel', mousewheel );
 
-	this.domElement.addEventListener( 'touchstart', touchstart );
-	this.domElement.addEventListener( 'touchend', touchend );
-	this.domElement.addEventListener( 'touchmove', touchmove );
+		this.domElement.addEventListener( 'touchstart', touchstart );
+		this.domElement.addEventListener( 'touchend', touchend );
+		this.domElement.addEventListener( 'touchmove', touchmove );
 
-	this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
-	this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
+		this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
+		this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
 
-	window.addEventListener( 'keydown', keydown );
-	window.addEventListener( 'keyup', keyup );
+		window.addEventListener( 'keydown', keydown );
+		window.addEventListener( 'keyup', keyup );
 
-	this.handleResize();
+		this.handleResize();
 
-	// force an update at start
-	this.update();
+		// force an update at start
+		this.update();
 
-};
+	}
 
-TrackballControls.prototype = Object.create( EventDispatcher.prototype );
-TrackballControls.prototype.constructor = TrackballControls;
+}
 
 export { TrackballControls };

File diff ditekan karena terlalu besar
+ 404 - 398
examples/jsm/controls/TransformControls.js


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini