Untitled diff

Created Diff never expires
135 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
404 lines
387 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
648 lines
/**
/**
* @author qiao / https://github.com/qiao
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author WestLangley / http://github.com/WestLangley
* @author erich666 / http://erichaines.com
*/
*/
/*global THREE, console */
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
// supported.
//
// Orbit - left mouse / touch: one finger move
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
// Pan - right mouse, or arrow keys / touch: three finter swipe
//
// This is a drop-in replacement for (most) TrackballControls used in examples.
// That is, include this js file and wherever you see:
// controls = new THREE.TrackballControls( camera );
// controls.target.z = 150;
// Simple substitute "OrbitControls" and the control should work as-is.
THREE.OrbitControls = function ( object, domElement ) {
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
// API
// Set to false to disable this control
this.enabled = true;
this.enabled = true;
this.center = new THREE.Vector3();
// "target" sets the location of focus, where the control orbits around
// and where it pans with respect to.
this.target = new THREE.Vector3();
this.userZoom = true;
// center is old, deprecated; use "target" instead
this.userZoomSpeed = 1.0;
this.center = this.target;
this.userRotate = true;
// This option actually enables dollying in and out; left as "zoom" for
this.userRotateSpeed = 1.0;
// backwards compatibility
this.noZoom = false;
this.zoomSpeed = 1.0;
this.userPan = true;
// Limits to how far you can dolly in and out
this.userPanSpeed = 2.0;
this.minDistance = 0;
this.maxDistance = Infinity;
// Set to true to disable this control
this.noRotate = false;
this.rotateSpeed = 1.0;
// Set to true to disable this control
this.noPan = false;
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
this.autoRotate = false;
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
this.maxPolarAngle = Math.PI; // radians
this.minDistance = 0;
// Set to true to disable use of the keys
this.maxDistance = Infinity;
this.noKeys = false;
// 65 /*A*/, 83 /*S*/, 68 /*D*/
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
////////////
// internals
// internals
var scope = this;
var scope = this;
var EPS = 0.000001;
var EPS = 0.000001;
var PIXELS_PER_ROUND = 1800;
var rotateStart = new THREE.Vector2();
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var zoomStart = new THREE.Vector2();
var panStart = new THREE.Vector2();
var zoomEnd = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var zoomDelta = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var panOffset = new THREE.Vector3();
var offset = new THREE.Vector3();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
var phiDelta = 0;
var phiDelta = 0;
var thetaDelta = 0;
var thetaDelta = 0;
var scale = 1;
var scale = 1;
var pan = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var lastQuaternion = new THREE.Quaternion();
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
var state = STATE.NONE;
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
// so camera.up is the orbit axis
var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
var quatInverse = quat.clone().inverse();
// events
// events
var changeEvent = { type: 'change' };
var changeEvent = { type: 'change' };
var startEvent = { type: 'start'};
var endEvent = { type: 'end'};
this.rotateLeft = function ( angle ) {
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
angle = getAutoRotationAngle();
}
}
thetaDelta -= angle;
thetaDelta -= angle;
};
};
this.rotateRight = function ( angle ) {
if ( angle === undefined ) {
Text moved with changes to lines 224-228 (90.3% similarity)
angle = getAutoRotationAngle();
}
thetaDelta += angle;
};
this.rotateUp = function ( angle ) {
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
angle = getAutoRotationAngle();
}
}
phiDelta -= angle;
phiDelta -= angle;
};
};
this.rotateDown = function ( angle ) {
// pass in distance in world space to move left
this.panLeft = function ( distance ) {
if ( angle === undefined ) {
var te = this.object.matrix.elements;
angle = getAutoRotationAngle();
// get X column of matrix
panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
panOffset.multiplyScalar( - distance );
pan.add( panOffset );
}
};
phiDelta += angle;
// pass in distance in world space to move up
this.panUp = function ( distance ) {
var te = this.object.matrix.elements;
// get Y column of matrix
panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
panOffset.multiplyScalar( distance );
pan.add( panOffset );
};
};
// pass in x,y of change desired in pixel space,
// right and down are positive
this.pan = function ( deltaX, deltaY ) {
this.zoomIn = function ( zoomScale ) {
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( zoomScale === undefined ) {
if ( scope.object.fov !== undefined ) {
zoomScale = getZoomScale();
// perspective
var position = scope.object.position;
var offset = position.clone().sub( scope.target );
var targetDistance = offset.length();
// half of the fov is center to top of screen
targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
// we actually don't use screenWidth, since perspective camera is fixed to screen height
scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
} else if ( scope.object.top !== undefined ) {
// orthographic
scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
} else {
// camera neither orthographic or perspective
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
}
}
scale /= zoomScale;
};
};
this.zoomOut = function ( zoomScale ) {
this.dollyIn = function ( dollyScale ) {
if ( zoomScale === undefined ) {
if ( dollyScale === undefined ) {
zoomScale = getZoomScale();
dollyScale = getZoomScale();
}
}
scale *= zoomScale;
scale /= dollyScale;
};
};
this.pan = function ( distance ) {
this.dollyOut = function ( dollyScale ) {
distance.transformDirection( this.object.matrix );
if ( dollyScale === undefined ) {
distance.multiplyScalar( scope.userPanSpeed );
Text moved with changes from lines 85-89 (90.3% similarity)
this.object.position.add( distance );
dollyScale = getZoomScale();
this.center.add( distance );
}
scale *= dollyScale;
};
};
this.update = function () {
this.update = function () {
var position = this.object.position;
var position = this.object.position;
var offset = position.clone().sub( this.center );
offset.copy( position ).sub( this.target );
// rotate offset to "y-axis-is-up" space
offset.applyQuaternion( quat );
// angle from z-axis around y-axis
// angle from z-axis around y-axis
var theta = Math.atan2( offset.x, offset.z );
var theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
// angle from y-axis
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( this.autoRotate ) {
if ( this.autoRotate ) {
this.rotateLeft( getAutoRotationAngle() );
this.rotateLeft( getAutoRotationAngle() );
}
}
theta += thetaDelta;
theta += thetaDelta;
phi += phiDelta;
phi += phiDelta;
// restrict phi to be between desired limits
// restrict phi to be between desired limits
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
var radius = offset.length() * scale;
// restrict radius to be between desired limits
// restrict radius to be between desired limits
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
// move target to panned location
this.target.add( pan );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
position.copy( this.center ).add( offset );
// rotate offset back to "camera-up-vector-is-up" space
offset.applyQuaternion( quatInverse );
this.object.lookAt( this.center );
position.copy( this.target ).add( offset );
this.object.lookAt( this.target );
thetaDelta = 0;
thetaDelta = 0;
phiDelta = 0;
phiDelta = 0;
scale = 1;
scale = 1;
pan.set( 0, 0, 0 );
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
// update condition is:
// min(camera displacement, camera rotation in radians)^2 > EPS
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
if ( lastPosition.distanceToSquared( this.object.position ) > EPS
|| 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) {
this.dispatchEvent( changeEvent );
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
lastPosition.copy( this.object.position );
lastQuaternion.copy (this.object.quaternion );
}
}
};
};
this.reset = function () {
state = STATE.NONE;
this.target.copy( this.target0 );
this.object.position.copy( this.position0 );
this.update();
};
function getAutoRotationAngle() {
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
}
function getZoomScale() {
function getZoomScale() {
return Math.pow( 0.95, scope.userZoomSpeed );
return Math.pow( 0.95, scope.zoomSpeed );
}
}
function onMouseDown( event ) {
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
event.preventDefault();
event.preventDefault();
if ( state === STATE.NONE )
if ( event.button === 0 ) {
{
if ( scope.noRotate === true ) return;
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;
state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
rotateStart.set( event.clientX, event.clientY );
} else if ( state === STATE.ZOOM ) {
} else if ( event.button === 1 ) {
if ( scope.noZoom === true ) return;
//state = STATE.ZOOM;
state = STATE.DOLLY;
zoomStart.set( event.clientX, event.clientY );
dollyStart.set( event.clientX, event.clientY );
} else if ( state === STATE.PAN ) {
} else if ( event.button === 2 ) {
if ( scope.noPan === true ) return;
//state = STATE.PAN;
state = STATE.PAN;
panStart.set( event.clientX, event.clientY );
}
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
document.addEventListener( 'mouseup', onMouseUp, false );
scope.dispatchEvent( startEvent );
}
}
function onMouseMove( event ) {
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
if ( scope.enabled === false ) return;
event.preventDefault();
event.preventDefault();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( state === STATE.ROTATE ) {
if ( state === STATE.ROTATE ) {
if ( scope.noRotate === true ) return;
rotateEnd.set( event.clientX, event.clientY );
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
rotateDelta.subVectors( rotateEnd, rotateStart );
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
// rotating across whole screen goes 360 degrees around
scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.ZOOM ) {
} else if ( state === STATE.DOLLY ) {
zoomEnd.set( event.clientX, event.clientY );
if ( scope.noZoom === true ) return;
zoomDelta.subVectors( zoomEnd, zoomStart );
if ( zoomDelta.y > 0 ) {
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
scope.zoomIn();
if ( dollyDelta.y > 0 ) {
scope.dollyIn();
} else {
} else {
scope.zoomOut();
scope.dollyOut();
}
}
zoomStart.copy( zoomEnd );
dollyStart.copy( dollyEnd );
} else if ( state === STATE.PAN ) {
} else if ( state === STATE.PAN ) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
if ( scope.noPan === true ) return;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
}
}
scope.update();
}
}
function onMouseUp( event ) {
function onMouseUp( /* event */ ) {
if ( scope.enabled === false ) return;
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
state = STATE.NONE;
}
}
function onMouseWheel( event ) {
function onMouseWheel( event ) {
if ( scope.enabled === false ) return;
if ( scope.enabled === false || scope.noZoom === true ) return;
if ( scope.userZoom === false ) return;
event.preventDefault();
event.stopPropagation();
var delta = 0;
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
delta = event.wheelDelta;
} else if ( event.detail ) { // Firefox
} else if ( event.detail !== undefined ) { // Firefox
delta = - event.detail;
delta = - event.detail;
}
}
if ( delta > 0 ) {
if ( delta > 0 ) {
scope.zoomOut();
scope.dollyOut();
} else {
} else {
scope.zoomIn();
scope.dollyIn();
}
}
scope.update();
scope.dispatchEvent( startEvent );
scope.dispatchEvent( endEvent );
}
}
function onKeyDown( event ) {
function onKeyDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
if ( scope.userPan === false ) return;
switch ( event.keyCode ) {
switch ( event.keyCode ) {
/*case scope.keys.UP:
case scope.keys.UP:
scope.pan( new THREE.Vector3( 0, 1, 0 ) );
scope.pan( 0, scope.keyPanSpeed );
scope.update();
break;
break;
case scope.keys.BOTTOM:
case scope.keys.BOTTOM:
scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
scope.pan( 0, - scope.keyPanSpeed );
scope.update();
break;
break;
case scope.keys.LEFT:
case scope.keys.LEFT:
scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
scope.pan( scope.keyPanSpeed, 0 );
scope.update();
break;
break;
case scope.keys.RIGHT:
case scope.keys.RIGHT:
scope.pan( new THREE.Vector3( 1, 0, 0 ) );
scope.pan( - scope.keyPanSpeed, 0 );
scope.update();
break;
break;
*/
case scope.keys.ROTATE:
}
state = STATE.ROTATE;
}
function touchstart( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) return;
state = STATE.TOUCH_ROTATE;
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
break;
case scope.keys.ZOOM:
state = STATE.ZOOM;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) return;
state = STATE.TOUCH_DOLLY;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
break;
break;
case scope.keys.PAN:
state = STATE.PAN;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) return;
state = STATE.TOUCH_PAN;
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
break;
default:
state = STATE.NONE;
}
}
scope.dispatchEvent( startEvent );
}
}
function onKeyUp( event ) {
switch ( event.keyCode ) {
function touchmove( event ) {
case scope.keys.ROTATE:
if ( scope.enabled === false ) return;
case scope.keys.ZOOM:
case scope.keys.PAN:
event.preventDefault();
event.stopPropagation();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) return;
if ( state !== STATE.TOUCH_ROTATE ) return;
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateDelta.subVectors( rotateEnd, rotateStart );
// rotating across whole screen goes 360 degrees around
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
break;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) return;
if ( state !== STATE.TOUCH_DOLLY ) return;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
dollyStart.copy( dollyEnd );
scope.update();
break;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) return;
if ( state !== STATE.TOUCH_PAN ) return;
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
break;
default:
state = STATE.NONE;
state = STATE.NONE;
break;
}
}
}
}
function touchend( /* event */ ) {
if ( scope.enabled === false ) return;
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
window.addEventListener( 'keydown', onKeyDown, false );
window.addEventListener( 'keydown', onKeyDown, false );
window.addEventListener( 'keyup', onKeyUp, false );
// force an update at start
this.update();
};
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );