Browse Source

Fixed scaling implementation in TransformControls.

Fixes #10916, #9312, #5489 and #5379

Fixed rotation at negative scale.

Merged TransformControls into one file and updated examples.

Added rotation snapping in local and world mode.

Cleaning up transform controls math and variables.
Aki Rodic 7 years ago
parent
commit
556d2248db

+ 4 - 6
editor/js/Viewport.js

@@ -92,7 +92,7 @@ var Viewport = function ( editor ) {
 
 		if ( object !== undefined ) {
 
-			switch ( transformControls.getMode() ) {
+			switch ( transformControls.mode ) {
 
 				case 'translate':
 
@@ -269,7 +269,6 @@ var Viewport = function ( editor ) {
 	var controls = new THREE.EditorControls( camera, container.dom );
 	controls.addEventListener( 'change', function () {
 
-		transformControls.update();
 		signals.cameraChanged.dispatch( camera );
 
 	} );
@@ -285,19 +284,19 @@ var Viewport = function ( editor ) {
 
 	signals.transformModeChanged.add( function ( mode ) {
 
-		transformControls.setMode( mode );
+		transformControls.mode = mode;
 
 	} );
 
 	signals.snapChanged.add( function ( dist ) {
 
-		transformControls.setTranslationSnap( dist );
+		transformControls.translationSnap = dist;
 
 	} );
 
 	signals.spaceChanged.add( function ( space ) {
 
-		transformControls.setSpace( space );
+		transformControls.space = space;
 
 	} );
 
@@ -391,7 +390,6 @@ var Viewport = function ( editor ) {
 		if ( editor.selected === object ) {
 
 			selectionBox.setFromObject( object );
-			transformControls.update();
 
 		}
 

+ 1083 - 177
examples/js/controls/TransformControls.js

@@ -4,9 +4,6 @@
 
 THREE.TransformControls = function ( camera, domElement ) {
 
-	// TODO: Make non-uniform scale and rotate play nice in hierarchies
-	// TODO: prevent infinite horizon translate
-
 	THREE.Object3D.call( this );
 
 	domElement = ( domElement !== undefined ) ? domElement : document;
@@ -21,6 +18,10 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 	var scope = this;
 
+	// Define properties with getters/setter
+	// Setting the defined property will automatically trigger change event
+	// Defined properties are passed down to gizmo and plane
+
 	defineProperty( "camera", camera );
 	defineProperty( "object", undefined );
 	defineProperty( "axis", null );
@@ -29,72 +30,88 @@ THREE.TransformControls = function ( camera, domElement ) {
 	defineProperty( "rotationSnap", null );
 	defineProperty( "space", "world" );
 	defineProperty( "size", 1 );
-
-	scope.dragging = false;
+	defineProperty( "dragging", false );
 
 	var changeEvent = { type: "change" };
 	var mouseDownEvent = { type: "mouseDown" };
 	var mouseUpEvent = { type: "mouseUp", mode: scope.mode };
 	var objectChangeEvent = { type: "objectChange" };
 
-	var ray = new THREE.Raycaster();
-	var pointerVector = new THREE.Vector2();
+	// Reusable utility variables
 
-	var point = new THREE.Vector3();
-	var offset = new THREE.Vector3();
-
-	var rotation = new THREE.Vector3();
-	var offsetRotation = new THREE.Vector3();
-
-	var lookAtMatrix = new THREE.Matrix4();
-	var _eye = new THREE.Vector3();
+	var _ray = new THREE.Raycaster();
+	var _pointerVector = new THREE.Vector2();
 
-	var _tempMatrix = new THREE.Matrix4();
 	var _tempVector = new THREE.Vector3();
+	var _tempVector2 = new THREE.Vector3();
 	var _tempQuaternion = 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 );
-	var _identityEuler = new THREE.Euler();
-
-	// var oldPosition = new THREE.Vector3();
-	// var oldScale = new THREE.Vector3();
-	// var oldRotationMatrix = new THREE.Matrix4();
-	var _worldRotationStart = new THREE.Quaternion();
-	var _worldPositionStart = new THREE.Vector3();
-	var _worldPointStart = new THREE.Vector3();
-	var _localPointStart = new THREE.Vector3();
-	var _localScale = new THREE.Vector3();
-	var _localPoint = new THREE.Vector3();
-	var _worldPoint = new THREE.Vector3();
-	var _worldShift = new THREE.Vector3();
-	var _localShift = new THREE.Vector3();
-	var _worldCross = new THREE.Vector3();
-	var _localCross = new THREE.Vector3();
-	var _worldQuaternion = new THREE.Quaternion();
-	var _localQuaternion = new THREE.Quaternion();
-
-	var _worldPosition = new THREE.Vector3();
-	var _worldRotation = new THREE.Euler();
+	var _unit = {
+		X: new THREE.Vector3( 1, 0, 0 ),
+		Y: new THREE.Vector3( 0, 1, 0 ),
+		Z: new THREE.Vector3( 0, 0, 1 )
+	}
+	var _identityQuaternion = new THREE.Quaternion();
+	var _alignVector = new THREE.Vector3();
+
+	var pointStart = new THREE.Vector3();
+	var pointEnd = new THREE.Vector3();
+	var rotationAxis = new THREE.Vector3();
+	var rotationAngle = 0;
+
+	var cameraPosition = new THREE.Vector3();
+	var cameraQuaternion = new THREE.Quaternion();
+	var cameraScale = new THREE.Vector3();
+
+	var parentPosition = new THREE.Vector3();
+	var parentQuaternion = new THREE.Quaternion();
+	var parentScale = new THREE.Vector3();
+
+	var worldPositionStart = new THREE.Vector3();
+	var worldQuaternionStart = new THREE.Quaternion();
+	var worldScaleStart = new THREE.Vector3();
+
+	var worldPosition = new THREE.Vector3();
+	var worldQuaternion = new THREE.Quaternion();
+	var worldScale = new THREE.Vector3();
+
+	var eye = new THREE.Vector3();
 
-	var alignVector = new THREE.Vector3();
 	var _positionStart = new THREE.Vector3();
 	var _quaternionStart = new THREE.Quaternion();
 	var _scaleStart = new THREE.Vector3();
 
-	domElement.addEventListener( "mousedown", onPointerDown, false );
-	domElement.addEventListener( "touchstart", onPointerDown, false );
-	domElement.addEventListener( "mousemove", onPointerHover, false );
-	domElement.addEventListener( "touchmove", onPointerHover, false );
-	domElement.addEventListener( "mousemove", onPointerMove, false );
-	domElement.addEventListener( "touchmove", onPointerMove, false );
-	domElement.addEventListener( "mouseup", onPointerUp, false );
-	domElement.addEventListener( "mouseleave", onPointerUp, false );
-	domElement.addEventListener( "mouseout", onPointerUp, false );
-	domElement.addEventListener( "touchend", onPointerUp, false );
-	domElement.addEventListener( "touchcancel", onPointerUp, false );
-	domElement.addEventListener( "touchleave", onPointerUp, false );
-	domElement.addEventListener( "contextmenu", onContext, false );
+	// TODO: remove properties unused in plane and gizmo
+
+	defineProperty( "parentQuaternion", parentQuaternion );
+	defineProperty( "worldPosition", worldPosition );
+	defineProperty( "worldPositionStart", worldPositionStart );
+	defineProperty( "worldQuaternion", worldQuaternion );
+	defineProperty( "worldQuaternionStart", worldQuaternionStart );
+	defineProperty( "cameraPosition", cameraPosition );
+	defineProperty( "cameraQuaternion", cameraQuaternion );
+	defineProperty( "pointStart", pointStart );
+	defineProperty( "pointEnd", pointEnd );
+	defineProperty( "rotationAxis", rotationAxis );
+	defineProperty( "rotationAngle", rotationAngle );
+	defineProperty( "eye", eye );
+
+	{
+
+		domElement.addEventListener( "mousedown", onPointerDown, false );
+		domElement.addEventListener( "touchstart", onPointerDown, false );
+		domElement.addEventListener( "mousemove", onPointerHover, false );
+		domElement.addEventListener( "touchmove", onPointerHover, false );
+		domElement.addEventListener( "mousemove", onPointerMove, false );
+		domElement.addEventListener( "touchmove", onPointerMove, false );
+		domElement.addEventListener( "mouseup", onPointerUp, false );
+		domElement.addEventListener( "mouseleave", onPointerUp, false );
+		domElement.addEventListener( "mouseout", onPointerUp, false );
+		domElement.addEventListener( "touchend", onPointerUp, false );
+		domElement.addEventListener( "touchcancel", onPointerUp, false );
+		domElement.addEventListener( "touchleave", onPointerUp, false );
+		domElement.addEventListener( "contextmenu", onContext, false );
+
+	}
 
 	this.dispose = function () {
 
@@ -114,6 +131,7 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 	};
 
+	// Set current object
 	this.attach = function ( object ) {
 
 		this.object = object;
@@ -121,6 +139,7 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 	};
 
+	// Detatch from object
 	this.detach = function () {
 
 		this.object = undefined;
@@ -129,6 +148,8 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 	};
 
+
+	// Defined getter, setter and store for a property
 	function defineProperty( propName, defaultValue ) {
 
 		var propValue = defaultValue;
@@ -146,6 +167,8 @@ THREE.TransformControls = function ( camera, domElement ) {
 				if ( propValue !== value ) {
 
 					propValue = value;
+					_plane[ propName ] = value;
+					_gizmo[ propName ] = value;
 
 					scope.dispatchEvent( changeEvent );
 
@@ -156,41 +179,30 @@ THREE.TransformControls = function ( camera, domElement ) {
 		});
 
 		scope[ propName ] = defaultValue;
+		_plane[ propName ] = defaultValue;
+		_gizmo[ propName ] = defaultValue;
 
 	}
 
+	// updateMatrixWorld  updates key transformation variables
 	this.updateMatrixWorld = function () {
 
 		if ( this.object === undefined ) return;
 
-		if ( this.mode === 'scale') this.space = 'local';
-
-		// camera.updateMatrixWorld();
-		// this.object.updateMatrixWorld();
+		this.camera.matrixWorld.decompose( cameraPosition, cameraQuaternion, cameraScale );
+		this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale );
+		this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale );
 
-		_worldPosition.setFromMatrixPosition( this.object.matrixWorld );
-		_worldRotation.setFromRotationMatrix( _tempMatrix.extractRotation( this.object.matrixWorld ) );
+		if ( this.camera instanceof THREE.PerspectiveCamera ) {
 
-		this.position.copy( _worldPosition );
+			eye.copy( cameraPosition ).sub( worldPosition ).normalize();
 
-		if ( camera instanceof THREE.PerspectiveCamera ) {
+		} else if ( this.camera instanceof THREE.OrthographicCamera ) {
 
-			_eye.setFromMatrixPosition( camera.matrixWorld ).sub( _worldPosition ).normalize();
-
-		} else if ( camera instanceof THREE.OrthographicCamera ) {
-
-			_eye.setFromMatrixPosition( camera.matrixWorld ).normalize();
+			eye.copy( cameraPosition ).normalize();
 
 		}
 
-		// TODO
-		this._worldPosition = _worldPosition;
-		this._worldRotation = _worldRotation;
-		this._eye = _eye;
-
-		var scale = _worldPosition.distanceTo( _tempVector.setFromMatrixPosition( camera.matrixWorld ) ) / 6 * this.size;
-		this.scale.set( scale, scale, scale );
-
 		THREE.Object3D.prototype.updateMatrixWorld.call( this );
 
 	};
@@ -207,7 +219,7 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 		var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
-		var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children );
+		var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children, scope.camera );
 
 		if ( intersect ) {
 
@@ -231,7 +243,7 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 		if ( pointer.button === 0 || pointer.button === undefined ) {
 
-			var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children );
+			var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children, scope.camera );
 
 			if ( intersect ) {
 
@@ -240,21 +252,44 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 				scope.axis = intersect.object.name;
 
-				_plane.update( scope.space === "local" ? _worldRotation : _identityEuler, _eye );
-
-				var planeIntersect = intersectObjects( pointer, [ _plane ] );
+				var planeIntersect = intersectObjects( pointer, [ _plane ], scope.camera );
 
 				if ( planeIntersect ) {
 
+					var space = scope.space;
+
+					if ( scope.mode === 'scale') {
+
+						space = 'local';
+
+					} else if ( scope.axis === 'E' ||  scope.axis === 'XYZE' ||  scope.axis === 'XYZ' ) {
+
+						space = 'world';
+
+					}
+
+					if ( space === 'local' && scope.mode === 'rotate' ) {
+
+						var snap = scope.rotationSnap;
+
+						if ( scope.axis === 'X' && snap ) scope.object.rotation.x = Math.round( scope.object.rotation.x / snap ) * snap;
+						if ( scope.axis === 'Y' && snap ) scope.object.rotation.y = Math.round( scope.object.rotation.y / snap ) * snap;
+						if ( scope.axis === 'Z' && snap ) scope.object.rotation.z = Math.round( scope.object.rotation.z / snap ) * snap;
+
+					}
+
+					scope.object.updateMatrixWorld();
+					scope.object.parent.updateMatrixWorld();
+
 					_positionStart.copy( scope.object.position );
 					_quaternionStart.copy( scope.object.quaternion );
 					_scaleStart.copy( scope.object.scale );
 
-					_worldRotationStart.setFromRotationMatrix( scope.object.matrixWorld );
-					_worldPositionStart.setFromMatrixPosition( scope.object.matrixWorld );
+					scope.object.matrixWorld.decompose( worldPositionStart, worldQuaternionStart, worldScaleStart );
+
+					pointStart.copy( planeIntersect.point ).sub( worldPositionStart );
 
-					_worldPointStart.copy( planeIntersect.point ).sub( _worldPositionStart );
-					_localPointStart.copy( _worldPointStart ).applyQuaternion( _worldRotationStart.clone().inverse() );
+					if ( space === 'local' ) pointStart.applyQuaternion( worldQuaternionStart.clone().inverse() );
 
 				}
 
@@ -268,6 +303,13 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 		scope.dragging = true;
 
+		if ( scope.axis !== null ) {
+
+			mouseDownEvent.mode = scope.mode;
+			scope.dispatchEvent( mouseDownEvent );
+
+		}
+
 	}
 
 	function onPointerMove( event ) {
@@ -277,51 +319,55 @@ THREE.TransformControls = function ( camera, domElement ) {
 		var object = scope.object;
 		var space = scope.space;
 
+		if ( mode === 'scale') {
+
+			space = 'local';
+
+		} else if ( axis === 'E' ||  axis === 'XYZE' ||  axis === 'XYZ' ) {
+
+			space = 'world';
+
+		}
+
 		if ( object === undefined || axis === null || scope.dragging === false || ( event.button !== undefined && event.button !== 0 ) ) return;
 
 		var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
-		var planeIntersect = intersectObjects( pointer, [ _plane ] );
+		var planeIntersect = intersectObjects( pointer, [ _plane ], scope.camera );
 
 		if ( planeIntersect === false ) return;
 
 		event.preventDefault();
 		event.stopPropagation();
 
-		_worldPoint.copy( planeIntersect.point ).sub( _worldPositionStart );
-		_worldShift.subVectors( _worldPoint, _worldPointStart );
+		pointEnd.copy( planeIntersect.point ).sub( worldPositionStart );
 
-		_localPoint.copy( _worldPoint );
-		_localPoint.applyQuaternion( _worldRotationStart.clone().inverse() );
-		_localShift.subVectors( _localPoint, _localPointStart );
-		_worldCross.copy( _worldPoint ).cross( _worldPointStart );
-		_localCross.copy( _localPoint ).cross( _localPointStart );
+		if ( space === 'local' ) pointEnd.applyQuaternion( worldQuaternionStart.clone().inverse() );
 
 		if ( mode === 'translate' ) {
 
 			if ( axis.search( 'X' ) === -1 ) {
-				_worldShift.x = 0;
-				_localShift.x = 0;
+				pointEnd.x = pointStart.x;
 			}
 			if ( axis.search( 'Y' ) === -1 ) {
-				_worldShift.y = 0;
-				_localShift.y = 0;
+				pointEnd.y = pointStart.y;
 			}
 			if ( axis.search( 'Z' ) === -1 ) {
-				_worldShift.z = 0;
-				_localShift.z = 0;
+				pointEnd.z = pointStart.z;
 			}
 
 			// Apply translate
 
 			if ( space === 'local' ) {
-				object.position.copy(_localShift).applyQuaternion( _quaternionStart );
+				object.position.copy( pointEnd ).sub( pointStart ).applyQuaternion( _quaternionStart );
 			} else {
-				object.position.copy( _worldShift );
+				object.position.copy( pointEnd ).sub( pointStart );
 			}
 
 			object.position.add( _positionStart );
 
+			// Apply translation snap
+
 			if ( scope.translationSnap ) {
 
 				if ( space === 'local' ) {
@@ -372,123 +418,104 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 		} else if ( mode === 'scale' ) {
 
-			if ( axis === 'XYZ' ) {
-
-				_localScale.set( _worldShift.y / 50, _worldShift.y / 50, _worldShift.y / 50 ).addScalar( 1 );
-
-			} else {
-
-				_localScale.set(
-					axis.search( 'X' ) !== -1 ? _localShift.x / 100 : 0,
-					axis.search( 'Y' ) !== -1 ? _localShift.y / 100 : 0,
-					axis.search( 'Z' ) !== -1 ? _localShift.z / 100 : 0
-				).addScalar( 1 );
+			if ( axis.search( 'XYZ' ) !== -1 ) {
 
-			}
-
-			// Apply scale
+				var d = pointEnd.length() / pointStart.length();
 
-			object.scale.copy( _scaleStart ).multiply( _localScale );
+				if ( pointEnd.dot( pointStart ) < 0 ) d *= -1;
 
-		} else if ( mode === 'rotate' ) {
-
-			if ( axis === 'E' ) {
-
-				_localCross.applyQuaternion( _worldRotationStart ).normalize();
-				direction = _localCross.dot( _eye ) < 0 ? 1 : -1;
-				_worldQuaternion.setFromAxisAngle( _eye, _localPoint.angleTo( _localPointStart ) * direction );
-
-			} else if ( axis === 'XYZE' ) {
-
-				// TODO: not working
-				// _tempVector.copy( _worldShift ).cross( _eye ).normalize();
-				// _worldQuaternion.setFromAxisAngle( _tempVector, -2 * offset.length() );
+				_tempVector.set( d, d, d );
 
 			} else {
 
-				var rotation = scope.space === "local" ? _worldRotation : _identityEuler;
+				_tempVector.copy( pointEnd ).divide( pointStart );
 
-				// TODO: make rotation speed relative to pointer movement in view space.
-				var LINEAR_ROTATION_SPEED = 0.005;
+				if ( axis.search( 'X' ) === -1 ) {
+					_tempVector.x = 1;
+				}
+				if ( axis.search( 'Y' ) === -1 ) {
+					_tempVector.y = 1;
+				}
+				if ( axis.search( 'Z' ) === -1 ) {
+					_tempVector.z = 1;
+				}
 
-				if ( axis === 'X' ) {
+			}
 
-					alignVector.set( 1, 0, 0 ).applyEuler( rotation );
+			// Apply scale
 
-					if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
+			object.scale.copy( _scaleStart ).multiply( _tempVector );
 
-						_localQuaternion.setFromAxisAngle( _unitX, _localPoint.angleTo( _localPointStart ) * ( _localCross.x > 0 ? -1 : 1 ) );
-						_worldQuaternion.setFromAxisAngle( _unitX, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.x > 0 ? -1 : 1 ) );
+		} else if ( mode === 'rotate' ) {
 
-          } else {
+			var LINEAR_ROTATION_SPEED = 10 / worldPosition.distanceTo( _tempVector.setFromMatrixPosition( scope.camera.matrixWorld ) );
 
-						_localQuaternion.setFromAxisAngle( _unitX, _worldPoint.sub( _worldPointStart ).dot( _unitX.clone().applyQuaternion( _worldRotationStart ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
-						_worldQuaternion.setFromAxisAngle( _unitX, _worldPoint.sub( _worldPointStart ).dot( _unitX.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
+			var quaternion = scope.space === "local" ? worldQuaternion : _identityQuaternion;
 
-          }
+			var unit = _unit[ axis ];
 
-				} else if ( axis === 'Y' ) {
+			if ( axis === 'E' ) {
 
-					alignVector.set( 0, 1, 0 ).applyEuler( rotation );
+				_tempVector.copy( pointEnd ).cross( pointStart );
+				rotationAxis.copy( eye );
+				rotationAngle = pointEnd.angleTo( pointStart ) * ( _tempVector.dot( eye ) < 0 ? 1 : -1 );
 
-					if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
+			} else if ( axis === 'XYZE' ) {
 
-						_localQuaternion.setFromAxisAngle( _unitY, _localPoint.angleTo( _localPointStart ) * ( _localCross.y > 0 ? -1 : 1 ) );
-						_worldQuaternion.setFromAxisAngle( _unitY, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.y > 0 ? -1 : 1 ) );
+				_tempVector.copy( pointEnd ).sub( pointStart ).cross( eye ).normalize();
+				rotationAxis.copy( _tempVector );
+				rotationAngle = pointEnd.sub( pointStart ).dot( _tempVector.cross( eye ) ) * LINEAR_ROTATION_SPEED;
 
-          } else {
+			} else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) {
 
-						_localQuaternion.setFromAxisAngle( _unitY, _worldPoint.sub( _worldPointStart ).dot( _unitY.clone().applyEuler( rotation ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
-						_worldQuaternion.setFromAxisAngle( _unitY, _worldPoint.sub( _worldPointStart ).dot( _unitY.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
+				_alignVector.copy( unit ).applyQuaternion( quaternion );
 
-          }
+				rotationAxis.copy( unit );
 
-				} else if ( axis === 'Z' ) {
+				var normalToCamera = Math.abs( _alignVector.dot( eye ) ) > 0.3;
 
-					alignVector.set( 0, 0, 1 ).applyEuler( rotation );
+				if ( normalToCamera ) {
 
-					if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
+					_tempVector.copy( pointEnd ).cross( pointStart );
 
-						_localQuaternion.setFromAxisAngle( _unitZ, _localPoint.angleTo( _localPointStart ) * ( _localCross.z > 0 ? -1 : 1 ) );
-						_worldQuaternion.setFromAxisAngle( _unitZ, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.z > 0 ? -1 : 1 ) );
+					var flip = {
+						X: _tempVector.x > 0 ? -1 : 1,
+						Y: _tempVector.y > 0 ? -1 : 1,
+						Z: _tempVector.z > 0 ? -1 : 1
+					}
 
-          } else {
+					rotationAngle = pointEnd.angleTo( pointStart ) * flip[ axis ];
 
-						_localQuaternion.setFromAxisAngle( _unitZ, _worldPoint.sub( _worldPointStart ).dot( _unitZ.clone().applyEuler( rotation ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
-						_worldQuaternion.setFromAxisAngle( _unitZ, _worldPoint.sub( _worldPointStart ).dot( _unitZ.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
+				} else {
 
-					}
+					_tempVector = unit.clone().applyQuaternion( quaternion );
+					_tempVector2 = pointEnd.clone().sub( pointStart ).applyQuaternion( worldQuaternionStart );
+					rotationAngle = _tempVector2.dot( _tempVector.cross( eye ) ) * LINEAR_ROTATION_SPEED;
 
 				}
 
 			}
 
-			// Apply rotate
+			// Apply rotation snap
 
-			if ( axis === 'E' ||  axis === 'XYZE' ) {
+			if ( scope.rotationSnap ) rotationAngle = Math.round( rotationAngle / scope.rotationSnap ) * scope.rotationSnap;
 
-			  space = 'world';
+			this.rotationAngle = rotationAngle;
 
-			}
+			// Apply rotate
 
 			if ( space === 'local' ) {
 
 				object.quaternion.copy( _quaternionStart );
-				object.quaternion.multiply( _localQuaternion );
+				object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) );
 
 			} else {
 
-				object.quaternion.copy( _worldQuaternion );
+				object.quaternion.copy( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) );
 				object.quaternion.multiply( _quaternionStart );
 
 			}
 
-			if ( scope.snapAngle) {
-
-				// TODO: implement rotation snap
-
-			}
-
 		}
 
 		scope.dispatchEvent( changeEvent );
@@ -513,9 +540,7 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 		if ( 'TouchEvent' in window && event instanceof TouchEvent ) {
 
-			// Force "rollover"
-
-			scope.axis = null;
+			scope.axis = null; // Force "rollover"
 
 		} else {
 
@@ -525,16 +550,16 @@ THREE.TransformControls = function ( camera, domElement ) {
 
 	}
 
-	function intersectObjects( pointer, objects ) {
+	function intersectObjects( pointer, objects, camera ) {
 
 		var rect = domElement.getBoundingClientRect();
 		var x = ( pointer.clientX - rect.left ) / rect.width;
 		var y = ( pointer.clientY - rect.top ) / rect.height;
 
-		pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 );
-		ray.setFromCamera( pointerVector, camera );
+		_pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 );
+		_ray.setFromCamera( _pointerVector, camera );
 
-		var intersections = ray.intersectObjects( objects, true );
+		var intersections = _ray.intersectObjects( objects, true );
 
 		return intersections[ 0 ] ? intersections[ 0 ] : false;
 
@@ -605,3 +630,884 @@ THREE.TransformControls.prototype = Object.assign( Object.create( THREE.Object3D
   isTransformControls: true
 
 } );
+
+
+THREE.TransformControlsGizmo = function () {
+
+	'use strict';
+
+	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
+	});
+
+	var gizmoLineMaterial = new THREE.LineBasicMaterial({
+		depthTest: false,
+		depthWrite: false,
+		transparent: true,
+		linewidth: 1,
+		fog: false
+	});
+
+	// Make unique material for each axis/color
+
+	var matInvisible = gizmoMaterial.clone();
+	matInvisible.opacity = 0.15;
+
+	var matHelper = gizmoMaterial.clone();
+	matHelper.opacity = 0.15;
+
+	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 matWhiteTransperent = gizmoMaterial.clone();
+	matWhiteTransperent.opacity = 0.25;
+
+	var matYellowTransparent = matWhiteTransperent.clone();
+	matYellowTransparent.color.set( 0xffff00 );
+
+	var matCyanTransparent = matWhiteTransperent.clone();
+	matCyanTransparent.color.set( 0x00ffff );
+
+	var matMagentaTransparent = matWhiteTransperent.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 matLineBlue = gizmoLineMaterial.clone();
+	matLineBlue.color.set( 0x0000ff );
+
+	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.addAttribute('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 );
+
+		}
+
+		geometry.addAttribute('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
+
+	var TranslateHelperGeometry = function( radius, arc ) {
+
+		var geometry = new THREE.BufferGeometry()
+
+		geometry.addAttribute('position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
+
+		return geometry;
+
+	};
+
+	// Gizmo definitions - custom hierarchy definitions for setupGizmo() function
+
+	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 ), matWhiteTransperent ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
+		],
+		XY: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matYellowTransparent ), [ 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.PlaneBufferGeometry( 0.295, 0.295 ), matCyanTransparent ), [ 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.PlaneBufferGeometry( 0.295, 0.295 ), matMagentaTransparent ), [ 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.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ] ]
+		],
+		YZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
+		],
+		XZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 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( new CircleGeometry( 1, 0.5 ), matLineRed ) ],
+			[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ], 'linear' ],
+			[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matRed ), [ 0, 0, 1 ], null, [ 4, 1, 4 ], 'radial' ],
+		],
+		Y: [
+			[ new THREE.Line( new 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 ], 'linear' ],
+			[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matGreen ), [ 0, 0, 1 ], null, [ 1, 4, 4 ], 'radial' ],
+		],
+		Z: [
+			[ new THREE.Line( new 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 ], 'linear' ],
+			[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matBlue ), [ 1, 0, 0 ], null, [ 4, 1, 4 ], 'radial' ],
+		],
+		E: [
+			[ new THREE.Line( new 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( new 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 ), matWhiteTransperent ), [ 1.1, 0, 0 ] ],
+		],
+		XYZY: [
+			[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 1.1, 0 ] ],
+		],
+		XYZZ: [
+			[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 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 Object3D with gizmos described in custom hierarchy definition.
+
+	var setupGizmo = function( gizmoMap ) {
+
+		var gizmo = new THREE.Object3D();
+
+		for ( var name in gizmoMap ) {
+
+			for ( var i = gizmoMap[ name ].length; i --; ) {
+
+				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.
+				object.name = name;
+				object.tag = tag;
+
+				if (position) {
+					object.position.set(position[ 0 ], position[ 1 ], position[ 2 ]);
+				}
+				if (rotation) {
+					object.rotation.set(rotation[ 0 ], rotation[ 1 ], rotation[ 2 ]);
+				}
+				if (scale) {
+					object.scale.set(scale[ 0 ], scale[ 1 ], scale[ 2 ]);
+				}
+
+				object.updateMatrix();
+
+				var tempGeometry = object.geometry.clone();
+				tempGeometry.applyMatrix(object.matrix);
+				object.geometry = tempGeometry;
+
+				object.position.set( 0, 0, 0 );
+				object.rotation.set( 0, 0, 0 );
+				object.scale.set(1, 1, 1);
+
+				gizmo.add(object);
+
+			}
+
+		}
+
+		return gizmo;
+
+	};
+
+	// Reusable utility variables
+
+	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.updateMatrixWorld = function () {
+
+		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
+
+		this.gizmo[ "translate" ].visible = this.mode === "translate";
+		this.gizmo[ "rotate" ].visible = this.mode === "rotate";
+		this.gizmo[ "scale" ].visible = this.mode === "scale";
+
+		this.helper[ "translate" ].visible = this.mode === "translate";
+		this.helper[ "rotate" ].visible = this.mode === "rotate";
+		this.helper[ "scale" ].visible = this.mode === "scale";
+
+
+		var 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++ ) {
+
+			var handle = handles[i];
+
+			// hide aligned to camera
+
+			handle.visible = true;
+			handle.rotation.set( 0, 0, 0 );
+			handle.position.copy( this.worldPosition );
+
+			var eyeDistance = this.worldPosition.distanceTo( this.cameraPosition);
+			handle.scale.set( 1, 1, 1 ).multiplyScalar( eyeDistance * this.size / 7 );
+
+			// TODO: simplify helpers and consider decoupling from gizmo
+
+			if ( handle.tag === 'helper' ) {
+
+				handle.visible = false;
+
+				if ( handle.name === 'AXIS' ) {
+
+					handle.position.copy( this.worldPositionStart );
+					handle.visible = !!this.axis;
+
+					if ( this.axis === 'X' ) {
+
+						tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) );
+						handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
+
+						if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.visible = false;
+						}
+
+					}
+
+					if ( this.axis === 'Y' ) {
+
+						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 ) {
+							handle.visible = false;
+						}
+
+					}
+
+					if ( this.axis === 'Z' ) {
+
+						tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
+						handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
+
+						if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.visible = false;
+						}
+
+					}
+
+					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 );
+						handle.visible = this.dragging;
+
+					}
+
+					if ( this.axis === 'E' ) {
+
+						handle.visible = false;
+
+					}
+
+
+				} else if ( handle.name === 'START' ) {
+
+					handle.position.copy( this.worldPositionStart );
+					handle.visible = this.dragging;
+
+				} else if ( handle.name === 'END' ) {
+
+					handle.position.copy( this.worldPosition );
+					handle.visible = this.dragging;
+
+				} else if ( handle.name === 'DELTA' ) {
+
+					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().inverse() );
+					handle.scale.copy( tempVector );
+					handle.visible = this.dragging;
+
+				} else {
+
+					handle.quaternion.copy( quaternion );
+
+					if ( this.dragging ) {
+
+						handle.position.copy( this.worldPositionStart );
+
+					} else {
+
+						handle.position.copy( this.worldPosition );
+
+					}
+
+					if ( this.axis ) {
+
+						handle.visible = this.axis.search( handle.name ) !== -1;
+
+					}
+
+				}
+
+				// If updating helper, skip rest of the loop
+				continue;
+
+			}
+
+			// Align handles to current local or world rotation
+
+			handle.quaternion.copy( quaternion );
+
+			if ( this.mode === 'translate' || this.mode === 'scale' ) {
+
+				// Hide translate and scale axis facing the camera
+
+				if ( handle.name === 'X' || handle.name === 'XYZX' ) {
+					if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.99 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'Y' || handle.name === 'XYZY' ) {
+					if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.99 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'Z' || handle.name === 'XYZZ' ) {
+					if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.99 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'XY' ) {
+					if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < 0.2 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'YZ' ) {
+					if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < 0.2 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'XZ' ) {
+					if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < 0.2 ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+
+				// Flip translate and scale axis ocluded behind another axis
+
+				if ( handle.name.search( 'X' ) !== -1 ) {
+					if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < -0.4 ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.x *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
+
+				if ( handle.name.search( 'Y' ) !== -1 ) {
+					if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < -0.4 ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.y *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
+
+				if ( handle.name.search( 'Z' ) !== -1 ) {
+					if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < -0.4 ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.z *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
+
+			} else if ( this.mode === 'rotate' ) {
+
+				// switch between liner/radial quaternion handle affordances
+
+				if ( handle.name === 'X' ) {
+					if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.3 ) {
+						if ( handle.tag === 'linear' ) {
+							handle.visible = false;
+						}
+					} else if ( handle.tag === 'radial' ) {
+						handle.visible = false;
+					}
+				}
+
+				if ( handle.name === 'Y' ) {
+					if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.3 ) {
+						if ( handle.tag === 'linear' ) {
+							handle.visible = false;
+						}
+					} else if ( handle.tag === 'radial' ) {
+						handle.visible = false;
+					}
+				}
+
+				if ( handle.name === 'Z' ) {
+					if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.3 ) {
+						if ( handle.tag === 'linear' ) {
+							handle.visible = false;
+						}
+					} else if ( handle.tag === 'radial' ) {
+						handle.visible = false;
+					}
+				}
+
+				// Align handles to current local or world rotation
+
+				tempQuaternion2.copy( quaternion );
+				alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).inverse() );
+
+				if ( handle.name.search( "E" ) !== - 1 ) {
+
+					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 );
+
+				}
+
+				if ( handle.name === 'Y' ) {
+
+					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 );
+
+				}
+
+			}
+
+			// highlight selected axis
+
+			handle.material._opacity = handle.material._opacity || handle.material.opacity;
+			handle.material._color = handle.material._color || handle.material.color.clone();
+
+			handle.material.color.copy( handle.material._color );
+			handle.material.opacity = handle.material._opacity;
+
+			if ( this.axis ) {
+
+				if ( handle.name === this.axis ) {
+
+					handle.material.opacity *= 2.0;
+					handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
+
+				} else if ( this.axis.split('').some( function( a ) { return handle.name === a; } ) ) {
+
+					handle.material.opacity *= 2.0;
+					handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
+
+				} else {
+
+					handle.material.opacity *= 0.15;
+
+				}
+
+			}
+
+		}
+
+		THREE.Object3D.prototype.updateMatrixWorld.call( this );
+
+	};
+
+	this.setMode = function() {
+
+		console.warn( 'THREE.TransformControlsGizmo: setMode function has been depricated.' );
+
+	};
+
+};
+
+THREE.TransformControlsGizmo.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
+
+	constructor: THREE.TransformControlsGizmo,
+
+	isTransformControlsGizmo: true
+
+} );
+
+
+THREE.TransformControlsPlane = function () {
+
+	'use strict';
+
+	THREE.Mesh.call( this,
+		new THREE.PlaneBufferGeometry( 100000, 100000, 2, 2 ),
+		new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1 } )
+	);
+
+	this.type = 'TransformControlsPlane';
+
+	var unitX = new THREE.Vector3( 1, 0, 0 );
+	var unitY = new THREE.Vector3( 0, 1, 0 );
+	var unitZ = new THREE.Vector3( 0, 0, 1 );
+
+	var tempVector = new THREE.Vector3();
+	var dirVector = new THREE.Vector3();
+	var alignVector = new THREE.Vector3();
+	var tempMatrix = new THREE.Matrix4();
+	var camRotation = new THREE.Euler();
+	var identityQuaternion = new THREE.Quaternion();
+
+	this.updateMatrixWorld = function() {
+
+		var space = this.space;
+
+		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 );
+		unitY.set( 0, 1, 0 ).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.
+
+		alignVector.copy( unitY );
+
+		switch ( this.mode ) {
+			case 'translate':
+			case 'scale':
+				switch ( this.axis ) {
+					case 'X':
+						alignVector.copy( this.eye ).cross( unitX );
+						dirVector.copy( unitX ).cross( alignVector );
+						break;
+					case 'Y':
+						alignVector.copy( this.eye ).cross( unitY );
+						dirVector.copy( unitY ).cross( alignVector );
+						break;
+					case 'Z':
+						alignVector.copy( this.eye ).cross( unitZ );
+						dirVector.copy( unitZ ).cross( alignVector );
+						break;
+					case 'XY':
+						dirVector.copy( unitZ );
+						break;
+					case 'YZ':
+						dirVector.copy( unitX );
+						break;
+					case 'XZ':
+						alignVector.copy( unitZ );
+						dirVector.copy( unitY );
+						break;
+					case 'XYZ':
+					case 'E':
+						dirVector.set( 0, 0, 0 );
+						break;
+				}
+				break;
+			case 'rotate':
+			default:
+				// special case for rotate
+				dirVector.set( 0, 0, 0 );
+		}
+
+		if ( dirVector.length() === 0 ) {
+
+			// If in rotate mode, make the plane parallel to camera
+			this.quaternion.copy( this.cameraQuaternion );
+
+		} else {
+
+			tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector );
+
+			this.quaternion.setFromRotationMatrix( tempMatrix );
+
+		}
+
+		THREE.Object3D.prototype.updateMatrixWorld.call( this );
+
+	};
+
+};
+
+THREE.TransformControlsPlane.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
+
+	constructor: THREE.TransformControlsPlane,
+
+	isTransformControlsPlane: true
+
+} );

+ 0 - 587
examples/js/controls/TransformControlsGizmo.js

@@ -1,587 +0,0 @@
-/**
- * @author arodic / https://github.com/arodic
- */
-
-THREE.TransformControlsGizmo = function () {
-
-  'use strict';
-
-  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
-  });
-
-  var gizmoLineMaterial = new THREE.LineBasicMaterial({
-    depthTest: false,
-    depthWrite: false,
-    transparent: true,
-    linewidth: 1,
-    fog: false
-  });
-
-  var matInvisible = gizmoMaterial.clone();
-  matInvisible.opacity = 0.15;
-
-  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 matWhiteTransperent = gizmoMaterial.clone();
-  matWhiteTransperent.opacity = 0.25;
-
-  var matYellowTransparent = matWhiteTransperent.clone();
-  matYellowTransparent.color.set(0xffff00);
-
-  var matCyanTransparent = matWhiteTransperent.clone();
-  matCyanTransparent.color.set(0x00ffff);
-
-  var matMagentaTransparent = matWhiteTransperent.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 matLineBlue = gizmoLineMaterial.clone();
-  matLineBlue.color.set(0x0000ff);
-
-  var matLineYellow = gizmoLineMaterial.clone();
-  matLineYellow.color.set(0xffff00);
-
-  var matLineGray = gizmoLineMaterial.clone();
-  matLineGray.color.set(0x787878);
-
-  var matLineYellowTransparent = matLineYellow.clone();
-  matLineYellowTransparent.opacity = 0.25;
-
-  // shared objects
-
-  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.addAttribute('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);
-    }
-    geometry.addAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
-    return geometry;
-  };
-
-  // gizmos
-
-  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 ), matWhiteTransperent ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
-    ],
-    XY: [
-      [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matYellowTransparent ), [ 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.PlaneBufferGeometry( 0.295, 0.295 ), matCyanTransparent ), [ 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.PlaneBufferGeometry( 0.295, 0.295 ), matMagentaTransparent ), [ 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.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ] ]
-    ],
-    YZ: [
-      [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
-    ],
-    XZ: [
-      [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ -Math.PI / 2, 0, 0 ] ]
-    ]
-  };
-
-  var gizmoRotate = {
-    X: [
-      [ new THREE.Line( new CircleGeometry( 1, 0.5 ), matLineRed ) ],
-      [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ], 'linear' ],
-      [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matRed ), [ 0, 0, 1 ], null, [ 4, 1, 4 ], 'radial' ],
-    ],
-    Y: [
-      [ new THREE.Line( new 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 ], 'linear' ],
-      [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matGreen ), [ 0, 0, 1 ], null, [ 1, 4, 4 ], 'radial' ],
-    ],
-    Z: [
-      [ new THREE.Line( new 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 ], 'linear' ],
-      [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.03, 0 ), matBlue ), [ 1, 0, 0 ], null, [ 4, 1, 4 ], 'radial' ],
-    ],
-    E: [
-      [ new THREE.Line( new 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( new CircleGeometry( 0.99, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ] ]
-    ]
-  };
-
-  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 ), matWhiteTransperent ), [ 1.1, 0, 0 ] ],
-    ],
-    XYZY: [
-      [ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 1.1, 0 ] ],
-    ],
-    XYZZ: [
-      [ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 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 setupGizmo = function( gizmoMap ) {
-
-    var gizmo = new THREE.Object3D();
-
-    for ( var name in gizmoMap ) {
-
-      for ( var i = gizmoMap[ name ].length; i --; ) {
-
-        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 ];
-
-        object.name = name;
-        object.tag = tag;
-
-        if (position) {
-          object.position.set(position[ 0 ], position[ 1 ], position[ 2 ]);
-        }
-        if (rotation) {
-          object.rotation.set(rotation[ 0 ], rotation[ 1 ], rotation[ 2 ]);
-        }
-        if (scale) {
-          object.scale.set(scale[ 0 ], scale[ 1 ], scale[ 2 ]);
-        }
-
-        object.updateMatrix();
-
-        var tempGeometry = object.geometry.clone();
-        tempGeometry.applyMatrix(object.matrix);
-        object.geometry = tempGeometry;
-
-        object.position.set(0, 0, 0);
-        object.rotation.set(0, 0, 0);
-        object.scale.set(1, 1, 1);
-
-        gizmo.add(object);
-
-      }
-
-    }
-
-    return gizmo;
-
-  };
-
-  var vec1 = new THREE.Vector3( 0, 0, 0 );
-  var tempVector = new THREE.Vector3();
-  var alignVector = new THREE.Vector3( 0, 1, 0 );
-  var lookAtMatrix = new THREE.Matrix4();
-  var tempQuaternion = new THREE.Quaternion();
-  var tempQuaternion2 = new THREE.Quaternion();
-  var identityEuler = new THREE.Euler();
-
-  var unitX = new THREE.Vector3( 1, 0, 0 );
-  var unitY = new THREE.Vector3( 0, 1, 0 );
-  var unitZ = new THREE.Vector3( 0, 0, 1 );
-
-  this.gizmo = {};
-	this.picker = {};
-
-  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.picker[ "translate" ].visible = false;
-  this.picker[ "rotate" ].visible = false;
-  this.picker[ "scale" ].visible = false;
-
-  this.updateMatrixWorld = function () {
-
-    var rotation = this.parent.space === "local" ? this.parent._worldRotation : identityEuler;
-    var eye = this.parent._eye
-    var mode = this.parent.mode;
-    var axis = this.parent.axis;
-
-    this.gizmo[ "translate" ].visible = mode === "translate";
-    this.gizmo[ "rotate" ].visible = mode === "rotate";
-    this.gizmo[ "scale" ].visible = mode === "scale";
-
-    this.picker[ "translate" ].visible = false; // mode === "translate";
-    this.picker[ "rotate" ].visible = false; // mode === "rotate";
-    this.picker[ "scale" ].visible = false; // mode === "scale";
-
-    var handles = [];
-    handles = handles.concat( this.picker[ mode ].children );
-    handles = handles.concat( this.gizmo[ mode ].children );
-
-    for ( var i = 0; i < handles.length; i++ ) {
-
-      var handle = handles[i];
-
-      // hide aligned to camera
-
-      handle.visible = true;
-      handle.scale.set( 1, 1, 1 );
-      handle.position.set( 0, 0, 0 );
-      handle.rotation.set( 0, 0, 0 );
-
-      if ( mode === 'translate' || mode === 'scale' ) {
-
-        // Hide translate and scale axis facing the camera
-
-        if ( handle.name === 'X' || handle.name === 'XYZX' ) {
-          if ( Math.abs( alignVector.set( 1, 0, 0 ).applyEuler( rotation ).dot( eye ) ) > 0.99 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-        if ( handle.name === 'Y' || handle.name === 'XYZY' ) {
-          if ( Math.abs( alignVector.set( 0, 1, 0 ).applyEuler( rotation ).dot( eye ) ) > 0.99 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-        if ( handle.name === 'Z' || handle.name === 'XYZZ' ) {
-          if ( Math.abs( alignVector.set( 0, 0, 1 ).applyEuler( rotation ).dot( eye ) ) > 0.99 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-        if ( handle.name === 'XY' ) {
-          if ( Math.abs( alignVector.set( 0, 0, 1 ).applyEuler( rotation ).dot( eye ) ) < 0.2 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-        if ( handle.name === 'YZ' ) {
-          if ( Math.abs( alignVector.set( 1, 0, 0 ).applyEuler( rotation ).dot( eye ) ) < 0.2 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-        if ( handle.name === 'XZ' ) {
-          if ( Math.abs( alignVector.set( 0, 1, 0 ).applyEuler( rotation ).dot( eye ) ) < 0.2 ) {
-            handle.scale.set( 1e-3, 1e-3, 1e-3 );
-            handle.visible = false;
-          }
-        }
-
-        // Flip translate and scale axis ocluded behind another axis
-
-        if ( handle.name.search( 'X' ) !== -1 ) {
-          if ( alignVector.set( 1, 0, 0 ).applyEuler( rotation ).dot( eye ) < -0.4 ) {
-            if ( handle.tag === 'fwd' ) {
-              handle.visible = false;
-            } else {
-              handle.scale.x = -1;
-            }
-          } else if ( handle.tag === 'bwd' ) {
-            handle.visible = false;
-          }
-        }
-
-        if ( handle.name.search( 'Y' ) !== -1 ) {
-          if ( alignVector.set( 0, 1, 0 ).applyEuler( rotation ).dot( eye ) < -0.4 ) {
-            if ( handle.tag === 'fwd' ) {
-              handle.visible = false;
-            } else {
-              handle.scale.y = -1;
-            }
-          } else if ( handle.tag === 'bwd' ) {
-            handle.visible = false;
-          }
-        }
-
-        if ( handle.name.search( 'Z' ) !== -1 ) {
-          if ( alignVector.set( 0, 0, 1 ).applyEuler( rotation ).dot( eye ) < -0.4 ) {
-            if ( handle.tag === 'fwd' ) {
-              handle.visible = false;
-            } else {
-              handle.scale.z = -1;
-            }
-          } else if ( handle.tag === 'bwd' ) {
-            handle.visible = false;
-          }
-        }
-
-        // Align handles to current local or world rotation
-
-        handle.quaternion.setFromEuler( rotation );
-
-      } else if (mode === 'rotate') {
-
-        // switch between liner/radial rotation handle affordances
-
-        if ( handle.name === 'X' ) {
-          if ( Math.abs( alignVector.set( 1, 0, 0 ).applyEuler( rotation ).dot( eye ) ) > 0.3 ) {
-            if ( handle.tag === 'linear' ) {
-              handle.visible = false;
-            }
-          } else if ( handle.tag === 'radial' ) {
-            handle.visible = false;
-          }
-        }
-
-        if ( handle.name === 'Y' ) {
-          if ( Math.abs( alignVector.set( 0, 1, 0 ).applyEuler( rotation ).dot( eye ) ) > 0.3 ) {
-            if ( handle.tag === 'linear' ) {
-              handle.visible = false;
-            }
-          } else if ( handle.tag === 'radial' ) {
-            handle.visible = false;
-          }
-        }
-
-        if ( handle.name === 'Z' ) {
-          if ( Math.abs( alignVector.set( 0, 0, 1 ).applyEuler( rotation ).dot( eye ) ) > 0.3 ) {
-            if ( handle.tag === 'linear' ) {
-              handle.visible = false;
-            }
-          } else if ( handle.tag === 'radial' ) {
-            handle.visible = false;
-          }
-        }
-
-        // Align handles to current local or world rotation
-
-        tempQuaternion2.setFromEuler( rotation );
-        alignVector.copy( eye ).applyQuaternion( tempQuaternion.setFromEuler( rotation ).inverse());
-
-        if ( handle.name.search( "E" ) !== - 1 ) {
-
-          alignVector.set( 0, 1, 0 );
-          handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, alignVector ) );
-
-        }
-
-        if ( handle.name === 'X' ) {
-
-          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 );
-
-        }
-
-        if ( handle.name === 'Z' ) {
-
-          tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) );
-          tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
-          handle.quaternion.copy( tempQuaternion );
-
-        }
-
-      }
-
-      // highlight selected axis
-
-      handle.material._opacity = handle.material._opacity || handle.material.opacity;
-      handle.material._color = handle.material._color || handle.material.color.clone();
-
-      handle.material.color.copy( handle.material._color );
-      handle.material.opacity = handle.material._opacity;
-
-      if ( axis ) {
-
-        if ( handle.name === axis ) {
-
-          handle.material.opacity *= 2.0;
-          handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
-
-        } else if ( axis.split('').some( function( a ) { return handle.name === a; } ) ) {
-
-          handle.material.opacity *= 2.0;
-          handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
-
-        } else {
-
-          handle.material.opacity *= 0.15;
-
-        }
-
-      }
-
-    }
-
-		THREE.Object3D.prototype.updateMatrixWorld.call( this );
-
-	};
-
-  this.setMode = function() {
-
-    console.warn( 'THREE.TransformControlsGizmo: setMode function has been depricated.' );
-
-  };
-
-};
-
-THREE.TransformControlsGizmo.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
-
-  constructor: THREE.TransformControlsGizmo,
-
-  isTransformControlsGizmo: true
-
-} );

+ 0 - 113
examples/js/controls/TransformControlsPlane.js

@@ -1,113 +0,0 @@
-/**
- * @author arodic / https://github.com/arodic
- */
-
-THREE.TransformControlsPlane = function () {
-
-  'use strict';
-
-  THREE.Mesh.call( this,
-    new THREE.PlaneBufferGeometry( 1000, 1000, 2, 2 ),
-    new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1 } )
-  );
-
-	this.type = 'TransformControlsPlane';
-
-  var unitX = new THREE.Vector3( 1, 0, 0 );
-  var unitY = new THREE.Vector3( 0, 1, 0 );
-  var unitZ = new THREE.Vector3( 0, 0, 1 );
-
-	var dirVector = new THREE.Vector3();
-	var alignVector = new THREE.Vector3();
-  var tempMatrix = new THREE.Matrix4();
-  var camRotation = new THREE.Euler();
-
-  this.update = function( rotation, eye ) {
-
-    var axis = this.parent.axis;
-    var mode = this.parent.mode;
-
-    unitX.set( 1, 0, 0 ).applyEuler( rotation );
-    unitY.set( 0, 1, 0 ).applyEuler( rotation );
-    unitZ.set( 0, 0, 1 ).applyEuler( rotation );
-
-    alignVector.copy( unitY );
-
-    switch ( mode ) {
-      case 'translate':
-      case 'scale':
-      switch ( axis ) {
-        case 'X':
-          alignVector.copy( eye ).cross( unitX );
-          dirVector.copy( unitX ).cross( alignVector );
-          break;
-        case 'Y':
-          alignVector.copy( eye ).cross( unitY );
-          dirVector.copy( unitY ).cross( alignVector );
-          break;
-        case 'Z':
-          alignVector.copy( eye ).cross( unitZ );
-          dirVector.copy( unitZ ).cross( alignVector );
-          break;
-        case 'XY':
-          dirVector.copy( unitZ );
-          break;
-        case 'YZ':
-          dirVector.copy( unitX );
-          break;
-        case 'XZ':
-          dirVector.copy( unitY );
-          break;
-        case 'XYZ':
-        case 'E':
-          dirVector.set( 0,0,0 );
-          break;
-        }
-        break;
-      case 'rotate':
-      default:
-        switch ( axis ) {
-          // case 'X':
-          //   dirVector.copy( unitX );
-          //   break;
-          // case 'Y':
-          //   dirVector.copy( unitY );
-          //   break;
-          // case 'Z':
-          //   dirVector.copy( unitZ );
-          //   break;
-          default:
-            dirVector.set( 0,0,0 );
-            break;
-        }
-        break;
-    }
-
-    if ( dirVector.length() === 0 ) {
-
-      var camRotation = new THREE.Euler();
-    	// camRotation.setFromRotationMatrix( _tempMatrix.extractRotation( camera.matrixWorld ) );
-    	camRotation.setFromRotationMatrix( this.parent.camera.matrixWorld );
-      this.quaternion.setFromEuler( camRotation );
-
-    } else {
-
-      tempMatrix.lookAt( this.position, dirVector, alignVector );
-
-      this.quaternion.setFromRotationMatrix( tempMatrix );
-
-    }
-
-    this.updateMatrixWorld();
-
-  };
-
-};
-
-THREE.TransformControlsPlane.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
-
-  constructor: THREE.TransformControlsPlane,
-
-  isTransformControlsPlane: true
-
-} );

+ 10 - 12
examples/misc_animation_authoring.html

@@ -87,34 +87,34 @@
 					switch ( event.keyCode ) {
 
 						case 81: // Q
-							control.setSpace( control.space === "local" ? "world" : "local" );
+							control.space = control.space === "local" ? "world" : "local";
 							break;
 
 						case 17: // Ctrl
-							control.setTranslationSnap( 100 );
-							control.setRotationSnap( THREE.Math.degToRad( 15 ) );
+							control.translationSnap = 100;
+							control.rotationSnap = THREE.Math.degToRad( 15 );
 							break;
 
 						case 87: // W
-							control.setMode( "translate" );
+							control.mode = "translate";
 							break;
 
 						case 69: // E
-							control.setMode( "rotate" );
+							control.mode = "rotate";
 							break;
 
 						case 82: // R
-							control.setMode( "scale" );
+							control.mode = "scale";
 							break;
 
 						case 187:
 						case 107: // +, =, num+
-							control.setSize( control.size + 0.1 );
+							control.size = control.size + 0.1;
 							break;
 
 						case 189:
 						case 109: // -, _, num-
-							control.setSize( Math.max( control.size - 0.1, 0.1 ) );
+							control.size = Math.max( control.size - 0.1, 0.1 );
 							break;
 
 					}
@@ -126,8 +126,8 @@
 					switch ( event.keyCode ) {
 
 						case 17: // Ctrl
-							control.setTranslationSnap( null );
-							control.setRotationSnap( null );
+							control.translationSnap = null;
+							control.rotationSnap = null;
 							break;
 
 					}
@@ -170,8 +170,6 @@
 
 			function render() {
 
-				control.update();
-
 				renderer.render( scene, camera );
 
 			}

+ 7 - 14
examples/misc_controls_transform.html

@@ -31,10 +31,7 @@
 		</div>
 
 		<script src="../build/three.js"></script>
-		<script src="js/controls/EditorControls.js"></script>
 		<script src="js/controls/TransformControls.js"></script>
-		<script src="js/controls/TransformControlsPlane.js"></script>
-		<script src="js/controls/TransformControlsGizmo.js"></script>
 
 		<script>
 
@@ -70,21 +67,17 @@
 				var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
 				var material = new THREE.MeshLambertMaterial( { map: texture } );
 
+				var group = new THREE.Group();
+				scene.add( group );
 
 				var mesh = new THREE.Mesh( geometry, material );
-				scene.add( mesh );
 
-				var control2 = new THREE.EditorControls( camera, renderer.domElement );
-				control2.addEventListener( 'change', render );
+				scene.add( mesh );
 
 				control = new THREE.TransformControls( camera, renderer.domElement );
-				control.addEventListener( 'change', function () {
-					control2.enabled = !control.dragging;
-					render();
-				} );
-				control.mode = "scale";
-				// control.space = "local";
-				mesh.rotation.set( 1, -2, -2 );
+				control.addEventListener( 'change', render );
+				control.mode = 'rotate'
+				control.space = 'local'
 
 				control.attach( mesh );
 				scene.add( control );
@@ -101,7 +94,7 @@
 
 						case 16: // Shift
 							control.translationSnap = 100;
-							control.rotationSnap = THREE.Math.degToRad( 15 );
+							control.rotationSnap = THREE.Math.degToRad( 45 );
 							break;
 
 						case 87: // W

+ 0 - 1
examples/webgl_geometry_spline_editor.html

@@ -414,7 +414,6 @@
 				requestAnimationFrame( animate );
 				render();
 				stats.update();
-				transformControl.update();
 
 			}