Last active
March 24, 2021 15:22
-
-
Save weisk/7ba02541dca08baa2de95e674681061d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author qiao / https://github.com/qiao | |
* @author mrdoob / http://mrdoob.com | |
* @author alteredq / http://alteredqualia.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.OrbitControls = function ( object, domElement ) { | |
this.object = object; | |
this.domElement = ( domElement !== undefined ) ? domElement : document; | |
// API | |
this.enabled = true; | |
this.center = new THREE.Vector3(); | |
this.userZoom = true; | |
this.userZoomSpeed = 1.0; | |
this.userRotate = true; | |
this.userRotateSpeed = 1.0; | |
this.userPan = true; | |
this.userPanSpeed = 2.0; | |
this.autoRotate = false; | |
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 | |
this.minPolarAngle = 0; // radians | |
this.maxPolarAngle = Math.PI; // radians | |
this.minDistance = 0; | |
this.maxDistance = Infinity; | |
// 65 /*A*/, 83 /*S*/, 68 /*D*/ | |
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 }; | |
// internals | |
var scope = this; | |
var EPS = 0.000001; | |
var PIXELS_PER_ROUND = 1800; | |
var rotateStart = new THREE.Vector2(); | |
var rotateEnd = new THREE.Vector2(); | |
var rotateDelta = new THREE.Vector2(); | |
var zoomStart = new THREE.Vector2(); | |
var zoomEnd = new THREE.Vector2(); | |
var zoomDelta = new THREE.Vector2(); | |
var phiDelta = 0; | |
var thetaDelta = 0; | |
var scale = 1; | |
var lastPosition = new THREE.Vector3(); | |
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 }; | |
var state = STATE.NONE; | |
// events | |
var changeEvent = { type: 'change' }; | |
this.rotateLeft = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
thetaDelta -= angle; | |
}; | |
this.rotateRight = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
thetaDelta += angle; | |
}; | |
this.rotateUp = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
phiDelta -= angle; | |
}; | |
this.rotateDown = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
phiDelta += angle; | |
}; | |
this.zoomIn = function ( zoomScale ) { | |
if ( zoomScale === undefined ) { | |
zoomScale = getZoomScale(); | |
} | |
scale /= zoomScale; | |
}; | |
this.zoomOut = function ( zoomScale ) { | |
if ( zoomScale === undefined ) { | |
zoomScale = getZoomScale(); | |
} | |
scale *= zoomScale; | |
}; | |
this.pan = function ( distance ) { | |
distance.transformDirection( this.object.matrix ); | |
distance.multiplyScalar( scope.userPanSpeed ); | |
this.object.position.add( distance ); | |
this.center.add( distance ); | |
}; | |
this.update = function () { | |
var position = this.object.position; | |
var offset = position.clone().sub( this.center ); | |
// angle from z-axis around y-axis | |
var theta = Math.atan2( offset.x, offset.z ); | |
// angle from y-axis | |
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); | |
if ( this.autoRotate ) { | |
this.rotateLeft( getAutoRotationAngle() ); | |
} | |
theta += thetaDelta; | |
phi += phiDelta; | |
// restrict phi to be between desired limits | |
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); | |
// restrict phi to be betwee EPS and PI-EPS | |
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); | |
var radius = offset.length() * scale; | |
// restrict radius to be between desired limits | |
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); | |
offset.x = radius * Math.sin( phi ) * Math.sin( theta ); | |
offset.y = radius * Math.cos( phi ); | |
offset.z = radius * Math.sin( phi ) * Math.cos( theta ); | |
position.copy( this.center ).add( offset ); | |
this.object.lookAt( this.center ); | |
thetaDelta = 0; | |
phiDelta = 0; | |
scale = 1; | |
if ( lastPosition.distanceTo( this.object.position ) > 0 ) { | |
this.dispatchEvent( changeEvent ); | |
lastPosition.copy( this.object.position ); | |
} | |
}; | |
function getAutoRotationAngle() { | |
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; | |
} | |
function getZoomScale() { | |
return Math.pow( 0.95, scope.userZoomSpeed ); | |
} | |
function onMouseDown( event ) { | |
if ( scope.enabled === false ) return; | |
if ( scope.userRotate === false ) return; | |
event.preventDefault(); | |
if ( state === STATE.NONE ) | |
{ | |
if ( event.button === 0 ) | |
state = STATE.ROTATE; | |
if ( event.button === 1 ) | |
state = STATE.ZOOM; | |
if ( event.button === 2 ) | |
state = STATE.PAN; | |
} | |
if ( state === STATE.ROTATE ) { | |
//state = STATE.ROTATE; | |
rotateStart.set( event.clientX, event.clientY ); | |
} else if ( state === STATE.ZOOM ) { | |
//state = STATE.ZOOM; | |
zoomStart.set( event.clientX, event.clientY ); | |
} else if ( state === STATE.PAN ) { | |
//state = STATE.PAN; | |
} | |
document.addEventListener( 'mousemove', onMouseMove, false ); | |
document.addEventListener( 'mouseup', onMouseUp, false ); | |
} | |
function onMouseMove( event ) { | |
if ( scope.enabled === false ) return; | |
event.preventDefault(); | |
if ( state === STATE.ROTATE ) { | |
rotateEnd.set( event.clientX, event.clientY ); | |
rotateDelta.subVectors( rotateEnd, rotateStart ); | |
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed ); | |
scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed ); | |
rotateStart.copy( rotateEnd ); | |
} else if ( state === STATE.ZOOM ) { | |
zoomEnd.set( event.clientX, event.clientY ); | |
zoomDelta.subVectors( zoomEnd, zoomStart ); | |
if ( zoomDelta.y > 0 ) { | |
scope.zoomIn(); | |
} else { | |
scope.zoomOut(); | |
} | |
zoomStart.copy( zoomEnd ); | |
} else if ( state === STATE.PAN ) { | |
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; | |
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; | |
scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) ); | |
} | |
} | |
function onMouseUp( event ) { | |
if ( scope.enabled === false ) return; | |
if ( scope.userRotate === false ) return; | |
document.removeEventListener( 'mousemove', onMouseMove, false ); | |
document.removeEventListener( 'mouseup', onMouseUp, false ); | |
state = STATE.NONE; | |
} | |
function onMouseWheel( event ) { | |
if ( scope.enabled === false ) return; | |
if ( scope.userZoom === false ) return; | |
var delta = 0; | |
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 | |
delta = event.wheelDelta; | |
} else if ( event.detail ) { // Firefox | |
delta = - event.detail; | |
} | |
if ( delta > 0 ) { | |
scope.zoomOut(); | |
} else { | |
scope.zoomIn(); | |
} | |
} | |
function onKeyDown( event ) { | |
if ( scope.enabled === false ) return; | |
if ( scope.userPan === false ) return; | |
switch ( event.keyCode ) { | |
/*case scope.keys.UP: | |
scope.pan( new THREE.Vector3( 0, 1, 0 ) ); | |
break; | |
case scope.keys.BOTTOM: | |
scope.pan( new THREE.Vector3( 0, - 1, 0 ) ); | |
break; | |
case scope.keys.LEFT: | |
scope.pan( new THREE.Vector3( - 1, 0, 0 ) ); | |
break; | |
case scope.keys.RIGHT: | |
scope.pan( new THREE.Vector3( 1, 0, 0 ) ); | |
break; | |
*/ | |
case scope.keys.ROTATE: | |
state = STATE.ROTATE; | |
break; | |
case scope.keys.ZOOM: | |
state = STATE.ZOOM; | |
break; | |
case scope.keys.PAN: | |
state = STATE.PAN; | |
break; | |
} | |
} | |
function onKeyUp( event ) { | |
switch ( event.keyCode ) { | |
case scope.keys.ROTATE: | |
case scope.keys.ZOOM: | |
case scope.keys.PAN: | |
state = STATE.NONE; | |
break; | |
} | |
} | |
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); | |
this.domElement.addEventListener( 'mousedown', onMouseDown, false ); | |
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); | |
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox | |
window.addEventListener( 'keydown', onKeyDown, false ); | |
window.addEventListener( 'keyup', onKeyUp, false ); | |
}; | |
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment