2021-03-20 19:08:15 +00:00
import {
EventDispatcher ,
MOUSE ,
Quaternion ,
Vector2 ,
Vector3
2021-04-20 01:31:32 +00:00
} from 'three/src/Three' ;
2021-03-20 19:08:15 +00:00
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 = 3.0 ;
this . zoomSpeed = 1.2 ;
2021-04-21 10:54:57 +00:00
this . panSpeed = 2 ;
2021-03-20 19:08:15 +00:00
this . noRotate = false ;
this . noZoom = false ;
this . noPan = false ;
this . staticMoving = true ;
this . dynamicDampingFactor = 0.2 ;
this . minDistance = 0 ;
this . maxDistance = Infinity ;
this . keys = [ 65 /*A*/ , 83 /*S*/ , 68 /*D*/ ] ;
2021-04-15 05:32:20 +00:00
this . mouseButtons = { LEFT : null , MIDDLE : MOUSE . PAN , RIGHT : MOUSE . ROTATE } ;
2021-03-20 19:08:15 +00:00
// internals
this . target = new Vector3 ( ) ;
var EPS = 0.000001 ;
var lastPosition = new Vector3 ( ) ;
var lastZoom = 1 ;
var _state = STATE . NONE ,
_keyState = STATE . NONE ,
_eye = new Vector3 ( ) ,
_movePrev = new Vector2 ( ) ,
_moveCurr = new Vector2 ( ) ,
_lastAxis = new Vector3 ( ) ,
_lastAngle = 0 ,
_zoomStart = new Vector2 ( ) ,
_zoomEnd = new Vector2 ( ) ,
_touchZoomDistanceStart = 0 ,
_touchZoomDistanceEnd = 0 ,
_panStart = new Vector2 ( ) ,
_panEnd = new 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 Vector2 ( ) ;
return function getMouseOnScreen ( pageX , pageY ) {
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 ( ) ;
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 . rotateCamera = ( function ( ) {
var axis = new Vector3 ( ) ,
quaternion = new Quaternion ( ) ,
eyeDirection = new Vector3 ( ) ,
objectUpDirection = new Vector3 ( ) ,
objectSidewaysDirection = new Vector3 ( ) ,
moveDirection = new Vector3 ( ) ,
angle ;
return function rotateCamera ( ) {
moveDirection . set ( _moveCurr . x - _movePrev . x , _moveCurr . y - _movePrev . y , 0 ) ;
angle = moveDirection . length ( ) ;
if ( angle ) {
_eye . copy ( scope . object . position ) . sub ( scope . target ) ;
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 ) ;
_eye . applyQuaternion ( quaternion ) ;
scope . object . up . applyQuaternion ( quaternion ) ;
_lastAxis . copy ( axis ) ;
_lastAngle = angle ;
} 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 ) ;
} ;
} ( ) ) ;
this . zoomCamera = function ( ) {
var factor ;
if ( _state === STATE . TOUCH _ZOOM _PAN ) {
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd ;
_touchZoomDistanceStart = _touchZoomDistanceEnd ;
if ( scope . object . isPerspectiveCamera ) {
_eye . multiplyScalar ( factor ) ;
} else if ( scope . object . isOrthographicCamera ) {
scope . object . zoom *= factor ;
scope . object . updateProjectionMatrix ( ) ;
} else {
console . warn ( 'THREE.TrackballControls: Unsupported camera type' ) ;
}
} else {
factor = 1.0 + ( _zoomEnd . y - _zoomStart . y ) * scope . zoomSpeed ;
if ( factor !== 1.0 && factor > 0.0 ) {
if ( scope . object . isPerspectiveCamera ) {
_eye . multiplyScalar ( factor ) ;
} else if ( scope . object . isOrthographicCamera ) {
scope . object . zoom /= factor ;
scope . object . updateProjectionMatrix ( ) ;
} else {
console . warn ( 'THREE.TrackballControls: Unsupported camera type' ) ;
}
}
if ( scope . staticMoving ) {
_zoomStart . copy ( _zoomEnd ) ;
} else {
_zoomStart . y += ( _zoomEnd . y - _zoomStart . y ) * this . dynamicDampingFactor ;
}
}
} ;
this . panCamera = ( function ( ) {
var mouseChange = new Vector2 ( ) ,
objectUp = new Vector3 ( ) ,
pan = new Vector3 ( ) ;
return function panCamera ( ) {
mouseChange . copy ( _panEnd ) . sub ( _panStart ) ;
if ( mouseChange . lengthSq ( ) ) {
if ( scope . object . isOrthographicCamera ) {
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 ;
}
2021-04-22 21:14:26 +00:00
// mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
mouseChange . multiplyScalar ( _eye . length ( ) * 1 / 577 * scope . domElement . clientWidth ) ;
2021-03-20 19:08:15 +00:00
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 ) ;
} else {
_panStart . add ( mouseChange . subVectors ( _panEnd , _panStart ) . multiplyScalar ( scope . dynamicDampingFactor ) ) ;
}
}
} ;
} ( ) ) ;
this . checkDistances = function ( ) {
if ( ! scope . noZoom || ! scope . noPan ) {
if ( _eye . lengthSq ( ) > scope . maxDistance * scope . maxDistance ) {
scope . object . position . addVectors ( scope . target , _eye . setLength ( scope . maxDistance ) ) ;
_zoomStart . copy ( _zoomEnd ) ;
}
if ( _eye . lengthSq ( ) < scope . minDistance * scope . minDistance ) {
scope . object . position . addVectors ( scope . target , _eye . setLength ( scope . minDistance ) ) ;
_zoomStart . copy ( _zoomEnd ) ;
}
}
} ;
this . update = function ( ) {
_eye . subVectors ( scope . object . position , scope . target ) ;
if ( ! scope . noRotate ) {
scope . rotateCamera ( ) ;
}
if ( ! scope . noZoom ) {
scope . zoomCamera ( ) ;
}
if ( ! scope . noPan ) {
scope . panCamera ( ) ;
}
scope . object . position . addVectors ( scope . target , _eye ) ;
if ( scope . object . isPerspectiveCamera ) {
scope . checkDistances ( ) ;
scope . object . lookAt ( scope . target ) ;
if ( lastPosition . distanceToSquared ( scope . object . position ) > EPS ) {
scope . dispatchEvent ( changeEvent ) ;
lastPosition . copy ( scope . object . position ) ;
}
} else if ( scope . object . isOrthographicCamera ) {
scope . object . lookAt ( scope . target ) ;
if ( lastPosition . distanceToSquared ( scope . object . position ) > EPS || lastZoom !== scope . object . zoom ) {
scope . dispatchEvent ( changeEvent ) ;
lastPosition . copy ( scope . object . position ) ;
lastZoom = scope . object . zoom ;
}
} else {
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
}
}
function onPointerMove ( event ) {
if ( scope . enabled === false ) return ;
switch ( event . pointerType ) {
case 'mouse' :
case 'pen' :
onMouseMove ( event ) ;
break ;
// TODO touch
}
}
function onPointerUp ( event ) {
if ( scope . enabled === false ) return ;
switch ( event . pointerType ) {
case 'mouse' :
case 'pen' :
onMouseUp ( event ) ;
break ;
// TODO touch
}
}
function keydown ( event ) {
if ( scope . enabled === false ) return ;
window . removeEventListener ( 'keydown' , keydown ) ;
if ( _keyState !== STATE . NONE ) {
return ;
} else if ( event . keyCode === scope . keys [ STATE . ROTATE ] && ! scope . noRotate ) {
_keyState = STATE . ROTATE ;
} else if ( event . keyCode === scope . keys [ STATE . ZOOM ] && ! scope . noZoom ) {
_keyState = STATE . ZOOM ;
} else if ( event . keyCode === scope . keys [ STATE . PAN ] && ! scope . noPan ) {
_keyState = STATE . PAN ;
}
}
function keyup ( ) {
if ( scope . enabled === false ) return ;
_keyState = STATE . NONE ;
window . addEventListener ( 'keydown' , keydown ) ;
}
function onMouseDown ( event ) {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
if ( _state === STATE . NONE ) {
switch ( event . button ) {
case 0 :
_state = scope . mouseButtons . LEFT ;
break ;
case 1 :
_state = scope . mouseButtons . MIDDLE ;
break ;
case 2 :
2021-04-15 05:32:20 +00:00
if ( event . ctrlKey ) {
_state = scope . mouseButtons . MIDDLE ;
} else {
_state = scope . mouseButtons . RIGHT ;
}
2021-03-20 19:08:15 +00:00
break ;
default :
_state = STATE . NONE ;
}
}
var state = ( _keyState !== STATE . NONE ) ? _keyState : _state ;
if ( state === STATE . ROTATE && ! scope . noRotate ) {
_moveCurr . copy ( getMouseOnCircle ( event . pageX , event . pageY ) ) ;
_movePrev . copy ( _moveCurr ) ;
} else if ( state === STATE . ZOOM && ! scope . noZoom ) {
_zoomStart . copy ( getMouseOnScreen ( event . pageX , event . pageY ) ) ;
_zoomEnd . copy ( _zoomStart ) ;
} else if ( state === STATE . PAN && ! scope . noPan ) {
_panStart . copy ( getMouseOnScreen ( event . pageX , event . pageY ) ) ;
_panEnd . copy ( _panStart ) ;
}
scope . domElement . ownerDocument . addEventListener ( 'pointermove' , onPointerMove ) ;
scope . domElement . ownerDocument . addEventListener ( 'pointerup' , onPointerUp ) ;
scope . dispatchEvent ( startEvent ) ;
}
function onMouseMove ( event ) {
if ( scope . enabled === false ) return ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
var state = ( _keyState !== STATE . NONE ) ? _keyState : _state ;
if ( state === STATE . ROTATE && ! scope . noRotate ) {
_movePrev . copy ( _moveCurr ) ;
_moveCurr . copy ( getMouseOnCircle ( event . pageX , event . pageY ) ) ;
} else if ( state === STATE . ZOOM && ! scope . noZoom ) {
_zoomEnd . copy ( getMouseOnScreen ( event . pageX , event . pageY ) ) ;
} else if ( state === STATE . PAN && ! scope . noPan ) {
_panEnd . copy ( getMouseOnScreen ( event . pageX , event . pageY ) ) ;
}
scope . update ( )
}
function onMouseUp ( event ) {
if ( scope . enabled === false ) return ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
_state = STATE . NONE ;
scope . domElement . ownerDocument . removeEventListener ( 'pointermove' , onPointerMove ) ;
scope . domElement . ownerDocument . removeEventListener ( 'pointerup' , onPointerUp ) ;
scope . dispatchEvent ( endEvent ) ;
}
function mousewheel ( event ) {
if ( scope . enabled === false ) return ;
if ( scope . noZoom === true ) return ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
switch ( event . deltaMode ) {
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 ;
}
scope . update ( )
scope . dispatchEvent ( startEvent ) ;
scope . dispatchEvent ( endEvent ) ;
}
function touchstart ( event ) {
if ( scope . enabled === false ) return ;
event . preventDefault ( ) ;
switch ( event . touches . length ) {
case 1 :
_state = STATE . TOUCH _ROTATE ;
_moveCurr . copy ( getMouseOnCircle ( event . touches [ 0 ] . pageX , event . touches [ 0 ] . pageY ) ) ;
_movePrev . copy ( _moveCurr ) ;
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 ) ;
}
function touchmove ( event ) {
if ( scope . enabled === false ) return ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
switch ( event . touches . length ) {
case 1 :
_movePrev . copy ( _moveCurr ) ;
_moveCurr . copy ( getMouseOnCircle ( event . touches [ 0 ] . pageX , event . touches [ 0 ] . pageY ) ) ;
break ;
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 ) ) ;
break ;
}
2021-04-22 08:30:03 +00:00
scope . update ( )
2021-03-20 19:08:15 +00:00
}
function touchend ( event ) {
if ( scope . enabled === false ) return ;
switch ( event . touches . length ) {
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 ;
}
scope . dispatchEvent ( endEvent ) ;
}
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 . 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 ( EventDispatcher . prototype ) ;
TrackballControls . prototype . constructor = TrackballControls ;
export { TrackballControls } ;