/**
* @author Eberhard Graether / http://egraether.com/*/THREE.TrackballControls= function( object, domElement ) {var _this = this;var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5};this.object =object;this.domElement = ( domElement !== undefined ) ?domElement : document;//API
this.enabled = true;this.screen = { width: 0, height: 0, offsetLeft: 0, offsetTop: 0};this.radius = ( this.screen.width + this.screen.height ) / 4;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 = [ 65 /*A*/, 83 /*S*/, 68 /*D*/];//internals
this.target = newTHREE.Vector3();var lastPosition = newTHREE.Vector3();var _state =STATE.NONE,
_prevState=STATE.NONE,
_eye= newTHREE.Vector3(),
_rotateStart= newTHREE.Vector3(),
_rotateEnd= newTHREE.Vector3(),
_zoomStart= newTHREE.Vector2(),
_zoomEnd= newTHREE.Vector2(),
_touchZoomDistanceStart= 0,
_touchZoomDistanceEnd= 0,
_panStart= newTHREE.Vector2(),
_panEnd= newTHREE.Vector2();//for reset
this.target0 = this.target.clone();this.position0 = this.object.position.clone();this.up0 = this.object.up.clone();//events
var changeEvent = { type: ‘change‘};//methods
this.handleResize = function() {this.screen.width =window.innerWidth;this.screen.height =window.innerHeight;this.screen.offsetLeft = 0;this.screen.offsetTop = 0;this.radius = ( this.screen.width + this.screen.height ) / 4;
};this.handleEvent = function( event ) {if ( typeof this[ event.type ] == ‘function‘) {this[ event.type ]( event );
}
};this.getMouseOnScreen = function( clientX, clientY ) {return newTHREE.Vector2(
( clientX- _this.screen.offsetLeft ) / _this.radius * 0.5,
( clientY- _this.screen.offsetTop ) / _this.radius * 0.5);
};this.getMouseProjectionOnBall = function( clientX, clientY ) {var mouseOnBall = newTHREE.Vector3(
( clientX- _this.screen.width * 0.5 - _this.screen.offsetLeft ) /_this.radius,
( _this.screen.height* 0.5 + _this.screen.offsetTop - clientY ) /_this.radius,0.0);var length =mouseOnBall.length();if ( length > 1.0) {
mouseOnBall.normalize();
}else{
mouseOnBall.z= Math.sqrt( 1.0 - length *length );
}
_eye.copy( _this.object.position ).sub( _this.target );var projection =_this.object.up.clone().setLength( mouseOnBall.y );
projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
projection.add( _eye.setLength( mouseOnBall.z ) );returnprojection;
};this.rotateCamera = function() {var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() /_rotateEnd.length() );if( angle ) {var axis = ( newTHREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize();
quaternion= newTHREE.Quaternion();
angle*=_this.rotateSpeed;
quaternion.setFromAxisAngle( axis,-angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_rotateEnd.applyQuaternion( quaternion );if( _this.staticMoving ) {
_rotateStart.copy( _rotateEnd );
}else{
quaternion.setFromAxisAngle( axis, angle* ( _this.dynamicDampingFactor - 1.0) );
_rotateStart.applyQuaternion( quaternion );
}
}
};this.zoomCamera = function() {if ( _state ===STATE.TOUCH_ZOOM ) {var factor = _touchZoomDistanceStart /_touchZoomDistanceEnd;
_touchZoomDistanceStart=_touchZoomDistanceEnd;
_eye.multiplyScalar( factor );
}else{var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) *_this.zoomSpeed;if ( factor !== 1.0 && factor > 0.0) {
_eye.multiplyScalar( factor );if( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
}else{
_zoomStart.y+= ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
}
};this.panCamera = function() {var mouseChange =_panEnd.clone().sub( _panStart );if( mouseChange.lengthSq() ) {
mouseChange.multiplyScalar( _eye.length()*_this.panSpeed );var pan =_eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
_this.object.position.add( pan );
_this.target.add( pan );if( _this.staticMoving ) {
_panStart=_panEnd;
}else{
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
}
}
};this.checkDistances = function() {if ( !_this.noZoom || !_this.noPan ) {if ( _this.object.position.lengthSq() > _this.maxDistance *_this.maxDistance ) {
_this.object.position.setLength( _this.maxDistance );
}if ( _eye.lengthSq() < _this.minDistance *_this.minDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
}
}
};this.update = function() {
_eye.subVectors( _this.object.position, _this.target );if ( !_this.noRotate ) {
_this.rotateCamera();
}if ( !_this.noZoom ) {
_this.zoomCamera();
}if ( !_this.noPan ) {
_this.panCamera();
}
_this.object.position.addVectors( _this.target, _eye );
_this.checkDistances();
_this.object.lookAt( _this.target );if ( lastPosition.distanceToSquared( _this.object.position ) > 0) {
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
};this.reset = function() {
_state=STATE.NONE;
_prevState=STATE.NONE;
_this.target.copy( _this.target0 );
_this.object.position.copy( _this.position0 );
_this.object.up.copy( _this.up0 );
_eye.subVectors( _this.object.position, _this.target );
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
};//listeners
functionkeydown( event ) {if ( _this.enabled === false ) return;
window.removeEventListener(‘keydown‘, keydown );
_prevState=_state;if ( _state !==STATE.NONE ) {return;
}else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
_state=STATE.ROTATE;
}else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
_state=STATE.ZOOM;
}else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
_state=STATE.PAN;
}
}functionkeyup( event ) {if ( _this.enabled === false ) return;
_state=_prevState;
window.addEventListener(‘keydown‘, keydown, false);
}functionmousedown( event ) {if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();if ( _state ===STATE.NONE ) {
_state=event.button;
}if ( _state === STATE.ROTATE && !_this.noRotate ) {
_rotateStart= _rotateEnd =_this.getMouseProjectionOnBall( event.clientX, event.clientY );
}else if ( _state === STATE.ZOOM && !_this.noZoom ) {
_zoomStart= _zoomEnd =_this.getMouseOnScreen( event.clientX, event.clientY );
}else if ( _state === STATE.PAN && !_this.noPan ) {
_panStart= _panEnd =_this.getMouseOnScreen( event.clientX, event.clientY );
}
document.addEventListener(‘mousemove‘, mousemove, false);
document.addEventListener(‘mouseup‘, mouseup, false);
}functionmousemove( event ) {if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();if ( _state === STATE.ROTATE && !_this.noRotate ) {
_rotateEnd=_this.getMouseProjectionOnBall( event.clientX, event.clientY );
}else if ( _state === STATE.ZOOM && !_this.noZoom ) {
_zoomEnd=_this.getMouseOnScreen( event.clientX, event.clientY );
}else if ( _state === STATE.PAN && !_this.noPan ) {
_panEnd=_this.getMouseOnScreen( event.clientX, event.clientY );
}
}functionmouseup( event ) {if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state=STATE.NONE;
document.removeEventListener(‘mousemove‘, mousemove );
document.removeEventListener(‘mouseup‘, mouseup );
}functionmousewheel( event ) {if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();var delta = 0;if ( event.wheelDelta ) { //WebKit / Opera / Explorer 9
delta= event.wheelDelta / 40;
}else if ( event.detail ) { //Firefox
delta= - event.detail / 3;
}
_zoomStart.y+= delta * 0.01;
}functiontouchstart( event ) {if ( _this.enabled === false ) return;switch( event.touches.length ) {case 1:
_state=STATE.TOUCH_ROTATE;
_rotateStart= _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;case 2:
_state=STATE.TOUCH_ZOOM;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 );break;case 3:
_state=STATE.TOUCH_PAN;
_panStart= _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;default:
_state=STATE.NONE;
}
}functiontouchmove( event ) {if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();switch( event.touches.length ) {case 1:
_rotateEnd= _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;case 2: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 )break;case 3:
_panEnd= _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;default:
_state=STATE.NONE;
}
}functiontouchend( event ) {if ( _this.enabled === false ) return;switch( event.touches.length ) {case 1:
_rotateStart= _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;case 2:
_touchZoomDistanceStart= _touchZoomDistanceEnd = 0;break;case 3:
_panStart= _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0].pageY );break;
}
_state=STATE.NONE;
}this.domElement.addEventListener( ‘contextmenu‘, function ( event ) { event.preventDefault(); }, false);this.domElement.addEventListener( ‘mousedown‘, mousedown, false);this.domElement.addEventListener( ‘mousewheel‘, mousewheel, false);this.domElement.addEventListener( ‘DOMMouseScroll‘, mousewheel, false ); //firefox
this.domElement.addEventListener( ‘touchstart‘, touchstart, false);this.domElement.addEventListener( ‘touchend‘, touchend, false);this.domElement.addEventListener( ‘touchmove‘, touchmove, false);
window.addEventListener(‘keydown‘, keydown, false);
window.addEventListener(‘keyup‘, keyup, false);this.handleResize();
};
THREE.TrackballControls.prototype= Object.create( THREE.EventDispatcher.prototype );