diff --git a/docs/examples/en/controls/OrbitControls.html b/docs/examples/en/controls/OrbitControls.html index d9802445f67425..5c55164400bbb0 100644 --- a/docs/examples/en/controls/OrbitControls.html +++ b/docs/examples/en/controls/OrbitControls.html @@ -187,12 +187,12 @@

[property:Float minZoom]

[property:Object mouseButtons]

- This object contains references to the mouse buttons used for the controls. + This object contains references to the mouse actions used by the controls. controls.mouseButtons = { - LEFT: THREE.MOUSE.LEFT, - MIDDLE: THREE.MOUSE.MIDDLE, - RIGHT: THREE.MOUSE.RIGHT + LEFT: THREE.MOUSE.ROTATE, + MIDDLE: THREE.MOUSE.DOLLY, + RIGHT: THREE.MOUSE.PAN }

@@ -234,6 +234,17 @@

[property:Vector3 target]

the focus of the controls.

+

[property:Object touches]

+

+ This object contains references to the touch actions used by the controls. + +controls.touches = { + ONE: THREE.TOUCH.ROTATE, + TWO: THREE.TOUCH.DOLLY_PAN +} + +

+

[property:Float zoom0]

Used internally by the [method:saveState] and [method:reset] methods. diff --git a/examples/js/controls/MapControls.js b/examples/js/controls/MapControls.js deleted file mode 100644 index 186a1bf0a434d1..00000000000000 --- a/examples/js/controls/MapControls.js +++ /dev/null @@ -1,1155 +0,0 @@ -/** - * @author qiao / https://github.com/qiao - * @author mrdoob / http://mrdoob.com - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author erich666 / http://erichaines.com - * @author moroine / https://github.com/moroine - */ - -// This set of controls performs orbiting, dollying (zooming), and panning. -// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). -// This is very similar to OrbitControls, another set of touch behavior -// -// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate -// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish -// Pan - left mouse, or arrow keys / touch: one-finger move - -THREE.MapControls = function ( object, domElement ) { - - this.object = object; - - this.domElement = ( domElement !== undefined ) ? domElement : document; - - // Set to false to disable this control - this.enabled = true; - - // "target" sets the location of focus, where the object orbits around - this.target = new THREE.Vector3(); - - // How far you can dolly in and out ( PerspectiveCamera only ) - this.minDistance = 0; - this.maxDistance = Infinity; - - // How far you can zoom in and out ( OrthographicCamera only ) - this.minZoom = 0; - this.maxZoom = Infinity; - - // How far you can orbit vertically, upper and lower limits. - // Range is 0 to Math.PI radians. - this.minPolarAngle = 0; // radians - this.maxPolarAngle = Math.PI; // radians - - // How far you can orbit horizontally, upper and lower limits. - // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. - this.minAzimuthAngle = - Infinity; // radians - this.maxAzimuthAngle = Infinity; // radians - - // Set to true to enable damping (inertia) - // If damping is enabled, you must call controls.update() in your animation loop - this.enableDamping = false; - this.dampingFactor = 0.25; - - // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. - // Set to false to disable zooming - this.enableZoom = true; - this.zoomSpeed = 1.0; - - // Set to false to disable rotating - this.enableRotate = true; - this.rotateSpeed = 1.0; - - // Set to false to disable panning - this.enablePan = true; - this.panSpeed = 1.0; - this.screenSpacePanning = false; // if true, pan in screen-space - this.keyPanSpeed = 7.0; // pixels moved per arrow key push - - // Set to true to automatically rotate around the target - // If auto-rotate is enabled, you must call controls.update() in your animation loop - this.autoRotate = false; - this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 - - // Set to false to disable use of the keys - this.enableKeys = true; - - // The four arrow keys - this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; - - // Mouse buttons - this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT }; - - // for reset - this.target0 = this.target.clone(); - this.position0 = this.object.position.clone(); - this.zoom0 = this.object.zoom; - - // - // public methods - // - - this.getPolarAngle = function () { - - return spherical.phi; - - }; - - this.getAzimuthalAngle = function () { - - return spherical.theta; - - }; - - this.saveState = function () { - - scope.target0.copy( scope.target ); - scope.position0.copy( scope.object.position ); - scope.zoom0 = scope.object.zoom; - - }; - - this.reset = function () { - - scope.target.copy( scope.target0 ); - scope.object.position.copy( scope.position0 ); - scope.object.zoom = scope.zoom0; - - scope.object.updateProjectionMatrix(); - scope.dispatchEvent( changeEvent ); - - scope.update(); - - state = STATE.NONE; - - }; - - // this method is exposed, but perhaps it would be better if we can make it private... - this.update = function () { - - var offset = new THREE.Vector3(); - - // 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(); - - var lastPosition = new THREE.Vector3(); - var lastQuaternion = new THREE.Quaternion(); - - return function update() { - - var position = scope.object.position; - - offset.copy( position ).sub( scope.target ); - - // rotate offset to "y-axis-is-up" space - offset.applyQuaternion( quat ); - - // angle from z-axis around y-axis - spherical.setFromVector3( offset ); - - if ( scope.autoRotate && state === STATE.NONE ) { - - rotateLeft( getAutoRotationAngle() ); - - } - - spherical.theta += sphericalDelta.theta; - spherical.phi += sphericalDelta.phi; - - // restrict theta to be between desired limits - spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); - - // restrict phi to be between desired limits - spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); - - spherical.makeSafe(); - - - spherical.radius *= scale; - - // restrict radius to be between desired limits - spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); - - // move target to panned location - scope.target.add( panOffset ); - - offset.setFromSpherical( spherical ); - - // rotate offset back to "camera-up-vector-is-up" space - offset.applyQuaternion( quatInverse ); - - position.copy( scope.target ).add( offset ); - - scope.object.lookAt( scope.target ); - - if ( scope.enableDamping === true ) { - - sphericalDelta.theta *= ( 1 - scope.dampingFactor ); - sphericalDelta.phi *= ( 1 - scope.dampingFactor ); - - panOffset.multiplyScalar( 1 - scope.dampingFactor ); - - } else { - - sphericalDelta.set( 0, 0, 0 ); - - panOffset.set( 0, 0, 0 ); - - } - - scale = 1; - - // update condition is: - // min(camera displacement, camera rotation in radians)^2 > EPS - // using small-angle approximation cos(x/2) = 1 - x^2 / 8 - - if ( zoomChanged || - lastPosition.distanceToSquared( scope.object.position ) > EPS || - 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { - - scope.dispatchEvent( changeEvent ); - - lastPosition.copy( scope.object.position ); - lastQuaternion.copy( scope.object.quaternion ); - zoomChanged = false; - - return true; - - } - - return false; - - }; - - }(); - - this.dispose = function () { - - scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); - scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); - scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); - - scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); - scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); - scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); - - document.removeEventListener( 'mousemove', onMouseMove, false ); - document.removeEventListener( 'mouseup', onMouseUp, false ); - - window.removeEventListener( 'keydown', onKeyDown, false ); - - //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? - - }; - - // - // internals - // - - var scope = this; - - var changeEvent = { type: 'change' }; - var startEvent = { type: 'start' }; - var endEvent = { type: 'end' }; - - var STATE = { - NONE: 0, - ROTATE_UP: 1, - ROTATE_LEFT: 2, - ROTATE: 3, // ROTATE_UP | ROTATE_LEFT - DOLLY: 4, - DOLLY_ROTATE: 7, // ROTATE | DOLLY - PAN: 8, - DOLLY_PAN: 12, // DOLLY | PAN - }; - - var state = STATE.NONE; - - var EPS = 0.000001; - - // current position in spherical coordinates - var spherical = new THREE.Spherical(); - var sphericalDelta = new THREE.Spherical(); - - var scale = 1; - var panOffset = new THREE.Vector3(); - var zoomChanged = false; - - var rotateStart = new THREE.Vector2(); - var rotateStart2 = new THREE.Vector2(); - var rotateEnd = new THREE.Vector2(); - var rotateEnd2 = new THREE.Vector2(); - var rotateDelta = new THREE.Vector2(); - var rotateDelta2 = new THREE.Vector2(); - var rotateDeltaStartFingers = new THREE.Vector2(); - var rotateDeltaEndFingers = new THREE.Vector2(); - - var panStart = new THREE.Vector2(); - var panEnd = new THREE.Vector2(); - var panDelta = new THREE.Vector2(); - - var dollyStart = new THREE.Vector2(); - var dollyEnd = new THREE.Vector2(); - var dollyDelta = new THREE.Vector2(); - - function getAutoRotationAngle() { - - return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; - - } - - function getZoomScale() { - - return Math.pow( 0.95, scope.zoomSpeed ); - - } - - function rotateLeft( angle ) { - - sphericalDelta.theta -= angle; - - } - - function rotateUp( angle ) { - - sphericalDelta.phi -= angle; - - } - - var panLeft = function () { - - var v = new THREE.Vector3(); - - return function panLeft( distance, objectMatrix ) { - - v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix - v.multiplyScalar( - distance ); - - panOffset.add( v ); - - }; - - }(); - - var panUp = function () { - - var v = new THREE.Vector3(); - - return function panUp( distance, objectMatrix ) { - - if ( scope.screenSpacePanning === true ) { - - v.setFromMatrixColumn( objectMatrix, 1 ); - - } else { - - v.setFromMatrixColumn( objectMatrix, 0 ); - v.crossVectors( scope.object.up, v ); - - } - - v.multiplyScalar( distance ); - - panOffset.add( v ); - - }; - - }(); - - // deltaX and deltaY are in pixels; right and down are positive - var pan = function () { - - var offset = new THREE.Vector3(); - - return function pan( deltaX, deltaY ) { - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - if ( scope.object.isPerspectiveCamera ) { - - // perspective - var position = scope.object.position; - offset.copy( position ).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 use only clientHeight here so aspect ratio does not distort speed - panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); - panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); - - } else if ( scope.object.isOrthographicCamera ) { - - // orthographic - panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); - panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); - - } else { - - // camera neither orthographic nor perspective - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - pan disabled.' ); - scope.enablePan = false; - - } - - }; - - }(); - - function dollyIn( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale /= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - function dollyOut( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale *= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - // - // event callbacks - update the object state - // - - function handleMouseDownRotate( event ) { - - //console.log( 'handleMouseDownRotate' ); - - rotateStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownDolly( event ) { - - //console.log( 'handleMouseDownDolly' ); - - dollyStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownPan( event ) { - - //console.log( 'handleMouseDownPan' ); - - panStart.set( event.clientX, event.clientY ); - - } - - function handleMouseMoveRotate( event ) { - - //console.log( 'handleMouseMoveRotate' ); - - rotateEnd.set( event.clientX, event.clientY ); - - rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height - - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - - rotateStart.copy( rotateEnd ); - - scope.update(); - - } - - function handleMouseMoveDolly( event ) { - - //console.log( 'handleMouseMoveDolly' ); - - dollyEnd.set( event.clientX, event.clientY ); - - dollyDelta.subVectors( dollyEnd, dollyStart ); - - if ( dollyDelta.y > 0 ) { - - dollyIn( getZoomScale() ); - - } else if ( dollyDelta.y < 0 ) { - - dollyOut( getZoomScale() ); - - } - - dollyStart.copy( dollyEnd ); - - scope.update(); - - } - - function handleMouseMovePan( event ) { - - //console.log( 'handleMouseMovePan' ); - - panEnd.set( event.clientX, event.clientY ); - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); - - scope.update(); - - } - - function handleMouseUp( event ) { - - // console.log( 'handleMouseUp' ); - - } - - function handleMouseWheel( event ) { - - // console.log( 'handleMouseWheel' ); - - if ( event.deltaY < 0 ) { - - dollyOut( getZoomScale() ); - - } else if ( event.deltaY > 0 ) { - - dollyIn( getZoomScale() ); - - } - - scope.update(); - - } - - function handleKeyDown( event ) { - - //console.log( 'handleKeyDown' ); - - switch ( event.keyCode ) { - - case scope.keys.UP: - pan( 0, scope.keyPanSpeed ); - scope.update(); - break; - - case scope.keys.BOTTOM: - pan( 0, - scope.keyPanSpeed ); - scope.update(); - break; - - case scope.keys.LEFT: - pan( scope.keyPanSpeed, 0 ); - scope.update(); - break; - - case scope.keys.RIGHT: - pan( - scope.keyPanSpeed, 0 ); - scope.update(); - break; - - } - - } - - function handleTouchStartRotate( event ) { - - // console.log( 'handleTouchStartRotate' ); - - // First finger - rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - // Second finger - rotateStart2.set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY ); - - } - - function handleTouchStartDolly( event ) { - - if ( scope.enableZoom ) { - - // console.log( 'handleTouchStartDolly' ); - - 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 ); - - } - - } - - function handleTouchStartPan( event ) { - - if ( scope.enablePan ) { - - // console.log( 'handleTouchStartPan' ); - - panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - } - - } - - function handleTouchMoveRotate( event ) { - - if ( scope.enableRotate === false ) return; - if ( ( state & STATE.ROTATE ) === 0 ) return; - - // First finger - rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - // Second finger - rotateEnd2.set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY ); - - rotateDelta.subVectors( rotateEnd, rotateStart ); - rotateDelta2.subVectors( rotateEnd2, rotateStart2 ); - rotateDeltaStartFingers.subVectors( rotateStart2, rotateStart ); - rotateDeltaEndFingers.subVectors( rotateEnd2, rotateEnd ); - - if ( isRotateUp() ) { - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - // rotating up and down along whole screen attempts to go 360, but limited to 180 - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - - // Start rotateUp ==> disable all movement to prevent flickering - state = STATE.ROTATE_UP; - - } else if ( ( state & STATE.ROTATE_LEFT ) !== 0 ) { - - rotateLeft( ( rotateDeltaStartFingers.angle() - rotateDeltaEndFingers.angle() ) * scope.rotateSpeed ); - - } - - rotateStart.copy( rotateEnd ); - rotateStart2.copy( rotateEnd2 ); - - } - - function isRotateUp() { - - // At start, does the two fingers are aligned horizontally - if ( ! isHorizontal( rotateDeltaStartFingers ) ) { - - return false; - - } - - // At end, does the two fingers are aligned horizontally - if ( ! isHorizontal( rotateDeltaEndFingers ) ) { - - return false; - - } - - // does the first finger moved vertically between start and end - if ( ! isVertical( rotateDelta ) ) { - - return false; - - } - - // does the second finger moved vertically between start and end - if ( ! isVertical( rotateDelta2 ) ) { - - return false; - - } - - // Does the two finger moved in the same direction (prevent moving one finger vertically up while the other goes down) - return rotateDelta.dot( rotateDelta2 ) > 0; - - } - - var isHorizontal = function () { - - var precision = Math.sin( Math.PI / 6 ); - - return function isHorizontal( vector ) { - - return Math.abs( Math.sin( vector.angle() ) ) < precision; - - }; - - }(); - - var isVertical = function () { - - var precision = Math.cos( Math.PI / 2 - Math.PI / 6 ); - - return function isVertical( vector ) { - - return Math.abs( Math.cos( vector.angle() ) ) < precision; - - }; - - }(); - - function handleTouchMoveDolly( event ) { - - if ( scope.enableZoom === false ) return; - if ( ( state & STATE.DOLLY ) === 0 ) return; - - // console.log( 'handleTouchMoveDolly' ); - - 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.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); - - dollyIn( dollyDelta.y ); - - dollyStart.copy( dollyEnd ); - - } - - function handleTouchMovePan( event ) { - - if ( scope.enablePan === false ) return; - if ( ( state & STATE.PAN ) === 0 ) return; - - // console.log( 'handleTouchMovePan' ); - - panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); - - } - - function handleTouchEnd( event ) { - - //console.log( 'handleTouchEnd' ); - - } - - // - // event handlers - FSM: listen for events and reset state - // - - function onMouseDown( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( event.button ) { - - case scope.mouseButtons.LEFT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - } else { - - if ( scope.enablePan === false ) return; - - handleMouseDownPan( event ); - - state = STATE.PAN; - - } - - break; - - case scope.mouseButtons.MIDDLE: - - if ( scope.enableZoom === false ) return; - - handleMouseDownDolly( event ); - - state = STATE.DOLLY; - - break; - - case scope.mouseButtons.RIGHT: - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - break; - - } - - if ( state !== STATE.NONE ) { - - document.addEventListener( 'mousemove', onMouseMove, false ); - document.addEventListener( 'mouseup', onMouseUp, false ); - - scope.dispatchEvent( startEvent ); - - } - - } - - function onMouseMove( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( state ) { - - case STATE.ROTATE: - - if ( scope.enableRotate === false ) return; - - handleMouseMoveRotate( event ); - - break; - - case STATE.DOLLY: - - if ( scope.enableZoom === false ) return; - - handleMouseMoveDolly( event ); - - break; - - case STATE.PAN: - - if ( scope.enablePan === false ) return; - - handleMouseMovePan( event ); - - break; - - } - - } - - function onMouseUp( event ) { - - if ( scope.enabled === false ) return; - - handleMouseUp( event ); - - document.removeEventListener( 'mousemove', onMouseMove, false ); - document.removeEventListener( 'mouseup', onMouseUp, false ); - - scope.dispatchEvent( endEvent ); - - state = STATE.NONE; - - } - - function onMouseWheel( event ) { - - if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; - - event.preventDefault(); - event.stopPropagation(); - - scope.dispatchEvent( startEvent ); - - handleMouseWheel( event ); - - scope.dispatchEvent( endEvent ); - - } - - function onKeyDown( event ) { - - if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; - - handleKeyDown( event ); - - } - - function onTouchStart( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( event.touches.length ) { - - case 1: // one-fingered touch: pan - - if ( scope.enablePan === false ) return; - - handleTouchStartPan( event ); - - state = STATE.PAN; - - break; - - case 2: // two-fingered touch: rotate-dolly - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - - handleTouchStartRotate( event ); - handleTouchStartDolly( event ); - - state = STATE.DOLLY_ROTATE; - - break; - - default: - - state = STATE.NONE; - - } - - if ( state !== STATE.NONE ) { - - scope.dispatchEvent( startEvent ); - - } - - } - - function onTouchMove( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - event.stopPropagation(); - - switch ( event.touches.length ) { - - case 1: // one-fingered touch: pan - - if ( scope.enablePan === false ) return; - if ( state !== STATE.PAN ) return; // is this needed? - - handleTouchMovePan( event ); - - scope.update(); - - break; - - case 2: // two-fingered touch: rotate-dolly - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - if ( ( state & STATE.DOLLY_ROTATE ) === 0 ) return; // is this needed? - - handleTouchMoveRotate( event ); - handleTouchMoveDolly( event ); - - scope.update(); - - break; - - default: - - state = STATE.NONE; - - } - - } - - function onTouchEnd( event ) { - - if ( scope.enabled === false ) return; - - handleTouchEnd( event ); - - scope.dispatchEvent( endEvent ); - - state = STATE.NONE; - - } - - function onContextMenu( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - } - - // - - scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); - - scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); - scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); - - scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); - scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); - scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); - - window.addEventListener( 'keydown', onKeyDown, false ); - - // force an update at start - - this.update(); - -}; - -THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype ); -THREE.MapControls.prototype.constructor = THREE.MapControls; - -Object.defineProperties( THREE.MapControls.prototype, { - - center: { - - get: function () { - - console.warn( 'THREE.MapControls: .center has been renamed to .target' ); - return this.target; - - } - - }, - - // backward compatibility - - noZoom: { - - get: function () { - - console.warn( 'THREE.MapControls: .noZoom has been deprecated. Use .enableZoom instead.' ); - return ! this.enableZoom; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noZoom has been deprecated. Use .enableZoom instead.' ); - this.enableZoom = ! value; - - } - - }, - - noRotate: { - - get: function () { - - console.warn( 'THREE.MapControls: .noRotate has been deprecated. Use .enableRotate instead.' ); - return ! this.enableRotate; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noRotate has been deprecated. Use .enableRotate instead.' ); - this.enableRotate = ! value; - - } - - }, - - noPan: { - - get: function () { - - console.warn( 'THREE.MapControls: .noPan has been deprecated. Use .enablePan instead.' ); - return ! this.enablePan; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noPan has been deprecated. Use .enablePan instead.' ); - this.enablePan = ! value; - - } - - }, - - noKeys: { - - get: function () { - - console.warn( 'THREE.MapControls: .noKeys has been deprecated. Use .enableKeys instead.' ); - return ! this.enableKeys; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noKeys has been deprecated. Use .enableKeys instead.' ); - this.enableKeys = ! value; - - } - - }, - - staticMoving: { - - get: function () { - - console.warn( 'THREE.MapControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); - return ! this.enableDamping; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); - this.enableDamping = ! value; - - } - - }, - - dynamicDampingFactor: { - - get: function () { - - console.warn( 'THREE.MapControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); - return this.dampingFactor; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); - this.dampingFactor = value; - - } - - } - -} ); diff --git a/examples/js/controls/OrbitControls.js b/examples/js/controls/OrbitControls.js index 87841da4fbd10f..66d1c758a49664 100644 --- a/examples/js/controls/OrbitControls.js +++ b/examples/js/controls/OrbitControls.js @@ -4,6 +4,7 @@ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com + * @author ScieCode / http://github.com/sciecode */ // This set of controls performs orbiting, dollying (zooming), and panning. @@ -75,7 +76,10 @@ THREE.OrbitControls = function ( object, domElement ) { this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; // Mouse buttons - this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT }; + this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN }; // for reset this.target0 = this.target.clone(); @@ -250,7 +254,16 @@ THREE.OrbitControls = function ( object, domElement ) { var startEvent = { type: 'start' }; var endEvent = { type: 'end' }; - var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; + var STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; var state = STATE.NONE; @@ -581,26 +594,34 @@ THREE.OrbitControls = function ( object, domElement ) { //console.log( 'handleTouchStartRotate' ); - rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + if ( event.touches.length == 1 ) { - } + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - function handleTouchStartDollyPan( event ) { + } - //console.log( 'handleTouchStartDollyPan' ); + else { - if ( scope.enableZoom ) { + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + rotateStart.set( x, y ); - 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 ); + function handleTouchStartPan( event ) { - dollyStart.set( 0, distance ); + //console.log( 'handleTouchStartPan' ); + + if ( event.touches.length == 1 ) { + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); } - if ( scope.enablePan ) { + else { var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); @@ -611,11 +632,57 @@ THREE.OrbitControls = function ( object, domElement ) { } + function handleTouchStartDolly( event ) { + + //console.log( 'handleTouchStartDolly' ); + + 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 ); + + } + + function handleTouchStartDollyPan( event ) { + + //console.log( 'handleTouchStartDollyPan' ); + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enablePan ) handleTouchStartPan( event ); + + } + + function handleTouchStartDollyRotate( event ) { + + //console.log( 'handleTouchStartDollyRotate' ); + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enableRotate ) handleTouchStartRotate( event ); + + } + function handleTouchMoveRotate( event ) { //console.log( 'handleTouchMoveRotate' ); - rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + if ( event.touches.length == 1 ) { + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + else { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + rotateEnd.set( x, y ); + + } rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); @@ -627,47 +694,71 @@ THREE.OrbitControls = function ( object, domElement ) { rotateStart.copy( rotateEnd ); - scope.update(); + } + + function handleTouchMovePan( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + if ( event.touches.length == 1 ) { + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + else { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); } - function handleTouchMoveDollyPan( event ) { + function handleTouchMoveDolly( event ) { - //console.log( 'handleTouchMoveDollyPan' ); + //console.log( 'handleTouchMoveRotate' ); - if ( scope.enableZoom ) { + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - 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 ); - var distance = Math.sqrt( dx * dx + dy * dy ); + dollyEnd.set( 0, distance ); - dollyEnd.set( 0, distance ); + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); - dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + dollyIn( dollyDelta.y ); - dollyIn( dollyDelta.y ); + dollyStart.copy( dollyEnd ); - dollyStart.copy( dollyEnd ); + } - } + function handleTouchMoveDollyPan( event ) { - if ( scope.enablePan ) { + //console.log( 'handleTouchMoveDollyPan' ); - var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); - var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + if ( scope.enableZoom ) handleTouchMoveDolly( event ); - panEnd.set( x, y ); + if ( scope.enablePan ) handleTouchMovePan( event ); - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + } - pan( panDelta.x, panDelta.y ); + function handleTouchMoveDollyRotate( event ) { - panStart.copy( panEnd ); + //console.log( 'handleTouchMoveDollyPan' ); - } + if ( scope.enableZoom ) handleTouchMoveDolly( event ); - scope.update(); + if ( scope.enableRotate ) handleTouchMoveRotate( event ); } @@ -696,45 +787,115 @@ THREE.OrbitControls = function ( object, domElement ) { switch ( event.button ) { - case scope.mouseButtons.LEFT: + case 0: + + switch ( scope.mouseButtons.LEFT ) { + + case THREE.MOUSE.ROTATE: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + } else { - if ( scope.enablePan === false ) return; + if ( scope.enableRotate === false ) return; - handleMouseDownPan( event ); + handleMouseDownRotate( event ); - state = STATE.PAN; + state = STATE.ROTATE; - } else { + } - if ( scope.enableRotate === false ) return; + break; - handleMouseDownRotate( event ); + case THREE.MOUSE.PAN: - state = STATE.ROTATE; + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + break; + + default: + + state = STATE.NONE; } break; - case scope.mouseButtons.MIDDLE: - if ( scope.enableZoom === false ) return; + case 1: + + switch ( scope.mouseButtons.MIDDLE ) { + + case THREE.MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; - handleMouseDownDolly( event ); + break; - state = STATE.DOLLY; + + default: + + state = STATE.NONE; + + } break; - case scope.mouseButtons.RIGHT: + case 2: - if ( scope.enablePan === false ) return; + switch ( scope.mouseButtons.RIGHT ) { - handleMouseDownPan( event ); + case THREE.MOUSE.ROTATE: - state = STATE.PAN; + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + break; + + case THREE.MOUSE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + break; + + default: + + state = STATE.NONE; + + } break; @@ -833,23 +994,67 @@ THREE.OrbitControls = function ( object, domElement ) { switch ( event.touches.length ) { - case 1: // one-fingered touch: rotate + case 1: - if ( scope.enableRotate === false ) return; + switch ( scope.touches.ONE ) { + + case TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan( event ); + + state = STATE.TOUCH_PAN; - handleTouchStartRotate( event ); + break; - state = STATE.TOUCH_ROTATE; + default: + + state = STATE.NONE; + + } break; - case 2: // two-fingered touch: dolly-pan + case 2: - if ( scope.enableZoom === false && scope.enablePan === false ) return; + switch ( scope.touches.TWO ) { + + case TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; - handleTouchStartDollyPan( event ); + handleTouchStartDollyRotate( event ); - state = STATE.TOUCH_DOLLY_PAN; + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } break; @@ -874,24 +1079,46 @@ THREE.OrbitControls = function ( object, domElement ) { event.preventDefault(); event.stopPropagation(); - switch ( event.touches.length ) { + switch ( state ) { - case 1: // one-fingered touch: rotate + case STATE.TOUCH_ROTATE: if ( scope.enableRotate === false ) return; - if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? handleTouchMoveRotate( event ); + scope.update(); + + break; + + case STATE.TOUCH_PAN: + + if ( scope.enablePan === false ) return; + + handleTouchMovePan( event ); + + scope.update(); + break; - case 2: // two-fingered touch: dolly-pan + case STATE.TOUCH_DOLLY_PAN: if ( scope.enableZoom === false && scope.enablePan === false ) return; - if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? handleTouchMoveDollyPan( event ); + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchMoveDollyRotate( event ); + + scope.update(); + break; default: @@ -1068,3 +1295,26 @@ Object.defineProperties( THREE.OrbitControls.prototype, { } } ); + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// This is very similar to OrbitControls, another set of touch behavior +// +// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - left mouse, or arrow keys / touch: one-finger move + +THREE.MapControls = function ( object, domElement ) { + + THREE.OrbitControls.call( this, object, domElement ); + + this.mouseButtons.LEFT = THREE.MOUSE.PAN; + this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE; + + this.touches.ONE = THREE.TOUCH.PAN; + this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE; + +} + +THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.MapControls.prototype.constructor = THREE.MapControls; diff --git a/examples/jsm/controls/MapControls.d.ts b/examples/jsm/controls/MapControls.d.ts deleted file mode 100644 index bf34f96f0effba..00000000000000 --- a/examples/jsm/controls/MapControls.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - Camera, - EventDispatcher, - MOUSE, - Object3D, - Vector3 -} from '../../../src/Three'; - -export class MapControls extends EventDispatcher { - constructor(object: Camera, domElement?: HTMLElement); - - object: Camera; - domElement: HTMLElement | HTMLDocument; - - // API - enabled: boolean; - target: Vector3; - - enableZoom: boolean; - zoomSpeed: number; - minDistance: number; - maxDistance: number; - enableRotate: boolean; - rotateSpeed: number; - enablePan: boolean; - keyPanSpeed: number; - maxZoom: number; - minZoom: number; - panSpeed: number; - autoRotate: boolean; - autoRotateSpeed: number; - minPolarAngle: number; - maxPolarAngle: number; - minAzimuthAngle: number; - maxAzimuthAngle: number; - enableKeys: boolean; - screenSpacePanning: boolean; - keys: { LEFT: number; UP: number; RIGHT: number; BOTTOM: number }; - mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE }; - enableDamping: boolean; - dampingFactor: number; - target0: Vector3; - position0: Vector3; - zoom0: number; - - rotateLeft(angle?: number): void; - - rotateUp(angle?: number): void; - - panLeft(distance?: number): void; - - panUp(distance?: number): void; - - pan(deltaX: number, deltaY: number): void; - - dollyIn(dollyScale: number): void; - - dollyOut(dollyScale: number): void; - - saveState(): void; - - update(): boolean; - - reset(): void; - - dispose(): void; - - getPolarAngle(): number; - - getAzimuthalAngle(): number; -} diff --git a/examples/jsm/controls/MapControls.js b/examples/jsm/controls/MapControls.js deleted file mode 100644 index 973fa443b0031d..00000000000000 --- a/examples/jsm/controls/MapControls.js +++ /dev/null @@ -1,1166 +0,0 @@ -/** - * @author qiao / https://github.com/qiao - * @author mrdoob / http://mrdoob.com - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author erich666 / http://erichaines.com - * @author moroine / https://github.com/moroine - */ - -import { - EventDispatcher, - MOUSE, - Quaternion, - Spherical, - Vector2, - Vector3 -} from "../../../build/three.module.js"; - -// This set of controls performs orbiting, dollying (zooming), and panning. -// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). -// This is very similar to OrbitControls, another set of touch behavior -// -// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate -// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish -// Pan - left mouse, or arrow keys / touch: one-finger move - -var MapControls = function ( object, domElement ) { - - this.object = object; - - this.domElement = ( domElement !== undefined ) ? domElement : document; - - // Set to false to disable this control - this.enabled = true; - - // "target" sets the location of focus, where the object orbits around - this.target = new Vector3(); - - // How far you can dolly in and out ( PerspectiveCamera only ) - this.minDistance = 0; - this.maxDistance = Infinity; - - // How far you can zoom in and out ( OrthographicCamera only ) - this.minZoom = 0; - this.maxZoom = Infinity; - - // How far you can orbit vertically, upper and lower limits. - // Range is 0 to Math.PI radians. - this.minPolarAngle = 0; // radians - this.maxPolarAngle = Math.PI; // radians - - // How far you can orbit horizontally, upper and lower limits. - // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. - this.minAzimuthAngle = - Infinity; // radians - this.maxAzimuthAngle = Infinity; // radians - - // Set to true to enable damping (inertia) - // If damping is enabled, you must call controls.update() in your animation loop - this.enableDamping = false; - this.dampingFactor = 0.25; - - // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. - // Set to false to disable zooming - this.enableZoom = true; - this.zoomSpeed = 1.0; - - // Set to false to disable rotating - this.enableRotate = true; - this.rotateSpeed = 1.0; - - // Set to false to disable panning - this.enablePan = true; - this.panSpeed = 1.0; - this.screenSpacePanning = false; // if true, pan in screen-space - this.keyPanSpeed = 7.0; // pixels moved per arrow key push - - // Set to true to automatically rotate around the target - // If auto-rotate is enabled, you must call controls.update() in your animation loop - this.autoRotate = false; - this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 - - // Set to false to disable use of the keys - this.enableKeys = true; - - // The four arrow keys - this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; - - // Mouse buttons - this.mouseButtons = { LEFT: MOUSE.LEFT, MIDDLE: MOUSE.MIDDLE, RIGHT: MOUSE.RIGHT }; - - // for reset - this.target0 = this.target.clone(); - this.position0 = this.object.position.clone(); - this.zoom0 = this.object.zoom; - - // - // public methods - // - - this.getPolarAngle = function () { - - return spherical.phi; - - }; - - this.getAzimuthalAngle = function () { - - return spherical.theta; - - }; - - this.saveState = function () { - - scope.target0.copy( scope.target ); - scope.position0.copy( scope.object.position ); - scope.zoom0 = scope.object.zoom; - - }; - - this.reset = function () { - - scope.target.copy( scope.target0 ); - scope.object.position.copy( scope.position0 ); - scope.object.zoom = scope.zoom0; - - scope.object.updateProjectionMatrix(); - scope.dispatchEvent( changeEvent ); - - scope.update(); - - state = STATE.NONE; - - }; - - // this method is exposed, but perhaps it would be better if we can make it private... - this.update = function () { - - var offset = new Vector3(); - - // so camera.up is the orbit axis - var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) ); - var quatInverse = quat.clone().inverse(); - - var lastPosition = new Vector3(); - var lastQuaternion = new Quaternion(); - - return function update() { - - var position = scope.object.position; - - offset.copy( position ).sub( scope.target ); - - // rotate offset to "y-axis-is-up" space - offset.applyQuaternion( quat ); - - // angle from z-axis around y-axis - spherical.setFromVector3( offset ); - - if ( scope.autoRotate && state === STATE.NONE ) { - - rotateLeft( getAutoRotationAngle() ); - - } - - spherical.theta += sphericalDelta.theta; - spherical.phi += sphericalDelta.phi; - - // restrict theta to be between desired limits - spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); - - // restrict phi to be between desired limits - spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); - - spherical.makeSafe(); - - - spherical.radius *= scale; - - // restrict radius to be between desired limits - spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); - - // move target to panned location - scope.target.add( panOffset ); - - offset.setFromSpherical( spherical ); - - // rotate offset back to "camera-up-vector-is-up" space - offset.applyQuaternion( quatInverse ); - - position.copy( scope.target ).add( offset ); - - scope.object.lookAt( scope.target ); - - if ( scope.enableDamping === true ) { - - sphericalDelta.theta *= ( 1 - scope.dampingFactor ); - sphericalDelta.phi *= ( 1 - scope.dampingFactor ); - - panOffset.multiplyScalar( 1 - scope.dampingFactor ); - - } else { - - sphericalDelta.set( 0, 0, 0 ); - - panOffset.set( 0, 0, 0 ); - - } - - scale = 1; - - // update condition is: - // min(camera displacement, camera rotation in radians)^2 > EPS - // using small-angle approximation cos(x/2) = 1 - x^2 / 8 - - if ( zoomChanged || - lastPosition.distanceToSquared( scope.object.position ) > EPS || - 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { - - scope.dispatchEvent( changeEvent ); - - lastPosition.copy( scope.object.position ); - lastQuaternion.copy( scope.object.quaternion ); - zoomChanged = false; - - return true; - - } - - return false; - - }; - - }(); - - this.dispose = function () { - - scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); - scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); - scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); - - scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); - scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); - scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); - - document.removeEventListener( 'mousemove', onMouseMove, false ); - document.removeEventListener( 'mouseup', onMouseUp, false ); - - window.removeEventListener( 'keydown', onKeyDown, false ); - - //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? - - }; - - // - // internals - // - - var scope = this; - - var changeEvent = { type: 'change' }; - var startEvent = { type: 'start' }; - var endEvent = { type: 'end' }; - - var STATE = { - NONE: 0, - ROTATE_UP: 1, - ROTATE_LEFT: 2, - ROTATE: 3, // ROTATE_UP | ROTATE_LEFT - DOLLY: 4, - DOLLY_ROTATE: 7, // ROTATE | DOLLY - PAN: 8, - DOLLY_PAN: 12, // DOLLY | PAN - }; - - var state = STATE.NONE; - - var EPS = 0.000001; - - // current position in spherical coordinates - var spherical = new Spherical(); - var sphericalDelta = new Spherical(); - - var scale = 1; - var panOffset = new Vector3(); - var zoomChanged = false; - - var rotateStart = new Vector2(); - var rotateStart2 = new Vector2(); - var rotateEnd = new Vector2(); - var rotateEnd2 = new Vector2(); - var rotateDelta = new Vector2(); - var rotateDelta2 = new Vector2(); - var rotateDeltaStartFingers = new Vector2(); - var rotateDeltaEndFingers = new Vector2(); - - var panStart = new Vector2(); - var panEnd = new Vector2(); - var panDelta = new Vector2(); - - var dollyStart = new Vector2(); - var dollyEnd = new Vector2(); - var dollyDelta = new Vector2(); - - function getAutoRotationAngle() { - - return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; - - } - - function getZoomScale() { - - return Math.pow( 0.95, scope.zoomSpeed ); - - } - - function rotateLeft( angle ) { - - sphericalDelta.theta -= angle; - - } - - function rotateUp( angle ) { - - sphericalDelta.phi -= angle; - - } - - var panLeft = function () { - - var v = new Vector3(); - - return function panLeft( distance, objectMatrix ) { - - v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix - v.multiplyScalar( - distance ); - - panOffset.add( v ); - - }; - - }(); - - var panUp = function () { - - var v = new Vector3(); - - return function panUp( distance, objectMatrix ) { - - if ( scope.screenSpacePanning === true ) { - - v.setFromMatrixColumn( objectMatrix, 1 ); - - } else { - - v.setFromMatrixColumn( objectMatrix, 0 ); - v.crossVectors( scope.object.up, v ); - - } - - v.multiplyScalar( distance ); - - panOffset.add( v ); - - }; - - }(); - - // deltaX and deltaY are in pixels; right and down are positive - var pan = function () { - - var offset = new Vector3(); - - return function pan( deltaX, deltaY ) { - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - if ( scope.object.isPerspectiveCamera ) { - - // perspective - var position = scope.object.position; - offset.copy( position ).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 use only clientHeight here so aspect ratio does not distort speed - panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); - panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); - - } else if ( scope.object.isOrthographicCamera ) { - - // orthographic - panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); - panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); - - } else { - - // camera neither orthographic nor perspective - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - pan disabled.' ); - scope.enablePan = false; - - } - - }; - - }(); - - function dollyIn( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale /= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - function dollyOut( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale *= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: MapControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - // - // event callbacks - update the object state - // - - function handleMouseDownRotate( event ) { - - //console.log( 'handleMouseDownRotate' ); - - rotateStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownDolly( event ) { - - //console.log( 'handleMouseDownDolly' ); - - dollyStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownPan( event ) { - - //console.log( 'handleMouseDownPan' ); - - panStart.set( event.clientX, event.clientY ); - - } - - function handleMouseMoveRotate( event ) { - - //console.log( 'handleMouseMoveRotate' ); - - rotateEnd.set( event.clientX, event.clientY ); - - rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height - - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - - rotateStart.copy( rotateEnd ); - - scope.update(); - - } - - function handleMouseMoveDolly( event ) { - - //console.log( 'handleMouseMoveDolly' ); - - dollyEnd.set( event.clientX, event.clientY ); - - dollyDelta.subVectors( dollyEnd, dollyStart ); - - if ( dollyDelta.y > 0 ) { - - dollyIn( getZoomScale() ); - - } else if ( dollyDelta.y < 0 ) { - - dollyOut( getZoomScale() ); - - } - - dollyStart.copy( dollyEnd ); - - scope.update(); - - } - - function handleMouseMovePan( event ) { - - //console.log( 'handleMouseMovePan' ); - - panEnd.set( event.clientX, event.clientY ); - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); - - scope.update(); - - } - - function handleMouseUp( event ) { - - // console.log( 'handleMouseUp' ); - - } - - function handleMouseWheel( event ) { - - // console.log( 'handleMouseWheel' ); - - if ( event.deltaY < 0 ) { - - dollyOut( getZoomScale() ); - - } else if ( event.deltaY > 0 ) { - - dollyIn( getZoomScale() ); - - } - - scope.update(); - - } - - function handleKeyDown( event ) { - - //console.log( 'handleKeyDown' ); - - switch ( event.keyCode ) { - - case scope.keys.UP: - pan( 0, scope.keyPanSpeed ); - scope.update(); - break; - - case scope.keys.BOTTOM: - pan( 0, - scope.keyPanSpeed ); - scope.update(); - break; - - case scope.keys.LEFT: - pan( scope.keyPanSpeed, 0 ); - scope.update(); - break; - - case scope.keys.RIGHT: - pan( - scope.keyPanSpeed, 0 ); - scope.update(); - break; - - } - - } - - function handleTouchStartRotate( event ) { - - // console.log( 'handleTouchStartRotate' ); - - // First finger - rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - // Second finger - rotateStart2.set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY ); - - } - - function handleTouchStartDolly( event ) { - - if ( scope.enableZoom ) { - - // console.log( 'handleTouchStartDolly' ); - - 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 ); - - } - - } - - function handleTouchStartPan( event ) { - - if ( scope.enablePan ) { - - // console.log( 'handleTouchStartPan' ); - - panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - } - - } - - function handleTouchMoveRotate( event ) { - - if ( scope.enableRotate === false ) return; - if ( ( state & STATE.ROTATE ) === 0 ) return; - - // First finger - rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - // Second finger - rotateEnd2.set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY ); - - rotateDelta.subVectors( rotateEnd, rotateStart ); - rotateDelta2.subVectors( rotateEnd2, rotateStart2 ); - rotateDeltaStartFingers.subVectors( rotateStart2, rotateStart ); - rotateDeltaEndFingers.subVectors( rotateEnd2, rotateEnd ); - - if ( isRotateUp() ) { - - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - - // rotating up and down along whole screen attempts to go 360, but limited to 180 - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - - // Start rotateUp ==> disable all movement to prevent flickering - state = STATE.ROTATE_UP; - - } else if ( ( state & STATE.ROTATE_LEFT ) !== 0 ) { - - rotateLeft( ( rotateDeltaStartFingers.angle() - rotateDeltaEndFingers.angle() ) * scope.rotateSpeed ); - - } - - rotateStart.copy( rotateEnd ); - rotateStart2.copy( rotateEnd2 ); - - } - - function isRotateUp() { - - // At start, does the two fingers are aligned horizontally - if ( ! isHorizontal( rotateDeltaStartFingers ) ) { - - return false; - - } - - // At end, does the two fingers are aligned horizontally - if ( ! isHorizontal( rotateDeltaEndFingers ) ) { - - return false; - - } - - // does the first finger moved vertically between start and end - if ( ! isVertical( rotateDelta ) ) { - - return false; - - } - - // does the second finger moved vertically between start and end - if ( ! isVertical( rotateDelta2 ) ) { - - return false; - - } - - // Does the two finger moved in the same direction (prevent moving one finger vertically up while the other goes down) - return rotateDelta.dot( rotateDelta2 ) > 0; - - } - - var isHorizontal = function () { - - var precision = Math.sin( Math.PI / 6 ); - - return function isHorizontal( vector ) { - - return Math.abs( Math.sin( vector.angle() ) ) < precision; - - }; - - }(); - - var isVertical = function () { - - var precision = Math.cos( Math.PI / 2 - Math.PI / 6 ); - - return function isVertical( vector ) { - - return Math.abs( Math.cos( vector.angle() ) ) < precision; - - }; - - }(); - - function handleTouchMoveDolly( event ) { - - if ( scope.enableZoom === false ) return; - if ( ( state & STATE.DOLLY ) === 0 ) return; - - // console.log( 'handleTouchMoveDolly' ); - - 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.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); - - dollyIn( dollyDelta.y ); - - dollyStart.copy( dollyEnd ); - - } - - function handleTouchMovePan( event ) { - - if ( scope.enablePan === false ) return; - if ( ( state & STATE.PAN ) === 0 ) return; - - // console.log( 'handleTouchMovePan' ); - - panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); - - } - - function handleTouchEnd( event ) { - - //console.log( 'handleTouchEnd' ); - - } - - // - // event handlers - FSM: listen for events and reset state - // - - function onMouseDown( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( event.button ) { - - case scope.mouseButtons.LEFT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - } else { - - if ( scope.enablePan === false ) return; - - handleMouseDownPan( event ); - - state = STATE.PAN; - - } - - break; - - case scope.mouseButtons.MIDDLE: - - if ( scope.enableZoom === false ) return; - - handleMouseDownDolly( event ); - - state = STATE.DOLLY; - - break; - - case scope.mouseButtons.RIGHT: - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - break; - - } - - if ( state !== STATE.NONE ) { - - document.addEventListener( 'mousemove', onMouseMove, false ); - document.addEventListener( 'mouseup', onMouseUp, false ); - - scope.dispatchEvent( startEvent ); - - } - - } - - function onMouseMove( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( state ) { - - case STATE.ROTATE: - - if ( scope.enableRotate === false ) return; - - handleMouseMoveRotate( event ); - - break; - - case STATE.DOLLY: - - if ( scope.enableZoom === false ) return; - - handleMouseMoveDolly( event ); - - break; - - case STATE.PAN: - - if ( scope.enablePan === false ) return; - - handleMouseMovePan( event ); - - break; - - } - - } - - function onMouseUp( event ) { - - if ( scope.enabled === false ) return; - - handleMouseUp( event ); - - document.removeEventListener( 'mousemove', onMouseMove, false ); - document.removeEventListener( 'mouseup', onMouseUp, false ); - - scope.dispatchEvent( endEvent ); - - state = STATE.NONE; - - } - - function onMouseWheel( event ) { - - if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; - - event.preventDefault(); - event.stopPropagation(); - - scope.dispatchEvent( startEvent ); - - handleMouseWheel( event ); - - scope.dispatchEvent( endEvent ); - - } - - function onKeyDown( event ) { - - if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; - - handleKeyDown( event ); - - } - - function onTouchStart( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - switch ( event.touches.length ) { - - case 1: // one-fingered touch: pan - - if ( scope.enablePan === false ) return; - - handleTouchStartPan( event ); - - state = STATE.PAN; - - break; - - case 2: // two-fingered touch: rotate-dolly - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - - handleTouchStartRotate( event ); - handleTouchStartDolly( event ); - - state = STATE.DOLLY_ROTATE; - - break; - - default: - - state = STATE.NONE; - - } - - if ( state !== STATE.NONE ) { - - scope.dispatchEvent( startEvent ); - - } - - } - - function onTouchMove( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - event.stopPropagation(); - - switch ( event.touches.length ) { - - case 1: // one-fingered touch: pan - - if ( scope.enablePan === false ) return; - if ( state !== STATE.PAN ) return; // is this needed? - - handleTouchMovePan( event ); - - scope.update(); - - break; - - case 2: // two-fingered touch: rotate-dolly - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - if ( ( state & STATE.DOLLY_ROTATE ) === 0 ) return; // is this needed? - - handleTouchMoveRotate( event ); - handleTouchMoveDolly( event ); - - scope.update(); - - break; - - default: - - state = STATE.NONE; - - } - - } - - function onTouchEnd( event ) { - - if ( scope.enabled === false ) return; - - handleTouchEnd( event ); - - scope.dispatchEvent( endEvent ); - - state = STATE.NONE; - - } - - function onContextMenu( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - } - - // - - scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); - - scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); - scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); - - scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); - scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); - scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); - - window.addEventListener( 'keydown', onKeyDown, false ); - - // force an update at start - - this.update(); - -}; - -MapControls.prototype = Object.create( EventDispatcher.prototype ); -MapControls.prototype.constructor = MapControls; - -Object.defineProperties( MapControls.prototype, { - - center: { - - get: function () { - - console.warn( 'THREE.MapControls: .center has been renamed to .target' ); - return this.target; - - } - - }, - - // backward compatibility - - noZoom: { - - get: function () { - - console.warn( 'THREE.MapControls: .noZoom has been deprecated. Use .enableZoom instead.' ); - return ! this.enableZoom; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noZoom has been deprecated. Use .enableZoom instead.' ); - this.enableZoom = ! value; - - } - - }, - - noRotate: { - - get: function () { - - console.warn( 'THREE.MapControls: .noRotate has been deprecated. Use .enableRotate instead.' ); - return ! this.enableRotate; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noRotate has been deprecated. Use .enableRotate instead.' ); - this.enableRotate = ! value; - - } - - }, - - noPan: { - - get: function () { - - console.warn( 'THREE.MapControls: .noPan has been deprecated. Use .enablePan instead.' ); - return ! this.enablePan; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noPan has been deprecated. Use .enablePan instead.' ); - this.enablePan = ! value; - - } - - }, - - noKeys: { - - get: function () { - - console.warn( 'THREE.MapControls: .noKeys has been deprecated. Use .enableKeys instead.' ); - return ! this.enableKeys; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .noKeys has been deprecated. Use .enableKeys instead.' ); - this.enableKeys = ! value; - - } - - }, - - staticMoving: { - - get: function () { - - console.warn( 'THREE.MapControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); - return ! this.enableDamping; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); - this.enableDamping = ! value; - - } - - }, - - dynamicDampingFactor: { - - get: function () { - - console.warn( 'THREE.MapControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); - return this.dampingFactor; - - }, - - set: function ( value ) { - - console.warn( 'THREE.MapControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); - this.dampingFactor = value; - - } - - } - -} ); - -export { MapControls }; diff --git a/examples/jsm/controls/OrbitControls.d.ts b/examples/jsm/controls/OrbitControls.d.ts index 9a748ad2428789..89320e6f639cf1 100644 --- a/examples/jsm/controls/OrbitControls.d.ts +++ b/examples/jsm/controls/OrbitControls.d.ts @@ -1,4 +1,4 @@ -import { Camera, MOUSE, Object3D, Vector3 } from '../../../src/Three'; +import { Camera, MOUSE, Object3D, TOUCH, Vector3 } from '../../../src/Three'; export class OrbitControls { constructor(object: Camera, domElement?: HTMLElement); @@ -44,7 +44,89 @@ export class OrbitControls { enableKeys: boolean; keys: { LEFT: number; UP: number; RIGHT: number; BOTTOM: number; }; - mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE; }; + mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE; }; + touches: { ONE: TOUCH; TWO: TOUCH }; + + rotateLeft(angle?: number): void; + + rotateUp(angle?: number): void; + + panLeft(distance?: number): void; + + panUp(distance?: number): void; + + pan(deltaX: number, deltaY: number): void; + + dollyIn(dollyScale: number): void; + + dollyOut(dollyScale: number): void; + + update(): void; + + reset(): void; + + dispose(): void; + + getPolarAngle(): number; + + getAzimuthalAngle(): number; + + // EventDispatcher mixins + addEventListener(type: string, listener: (event: any) => void): void; + + hasEventListener(type: string, listener: (event: any) => void): boolean; + + removeEventListener(type: string, listener: (event: any) => void): void; + + dispatchEvent(event: { type: string; target: any; }): void; +} + +export class MapControls { + constructor(object: Camera, domElement?: HTMLElement); + + object: Camera; + domElement: HTMLElement | HTMLDocument; + + // API + enabled: boolean; + target: Vector3; + + // deprecated + center: Vector3; + + minDistance: number; + maxDistance: number; + + minZoom: number; + maxZoom: number; + + minPolarAngle: number; + maxPolarAngle: number; + + minAzimuthAngle: number; + maxAzimuthAngle: number; + + enableDamping: boolean; + dampingFactor: number; + + enableZoom: boolean; + zoomSpeed: number; + + enableRotate: boolean; + rotateSpeed: number; + + enablePan: boolean; + panSpeed: number; + screenSpacePanning: boolean; + keyPanSpeed: number; + + autoRotate: boolean; + autoRotateSpeed: number; + + enableKeys: boolean; + keys: { LEFT: number; UP: number; RIGHT: number; BOTTOM: number; }; + mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE; }; + touches: { ONE: TOUCH; TWO: TOUCH }; rotateLeft(angle?: number): void; diff --git a/examples/jsm/controls/OrbitControls.js b/examples/jsm/controls/OrbitControls.js index f1d3ebc6d23c0d..8532cc1b6e76a7 100644 --- a/examples/jsm/controls/OrbitControls.js +++ b/examples/jsm/controls/OrbitControls.js @@ -4,6 +4,7 @@ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com + * @author ScieCode / http://github.com/sciecode */ import { @@ -11,6 +12,7 @@ import { MOUSE, Quaternion, Spherical, + TOUCH, Vector2, Vector3 } from "../../../build/three.module.js"; @@ -84,7 +86,10 @@ var OrbitControls = function ( object, domElement ) { this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; // Mouse buttons - this.mouseButtons = { LEFT: MOUSE.LEFT, MIDDLE: MOUSE.MIDDLE, RIGHT: MOUSE.RIGHT }; + this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; // for reset this.target0 = this.target.clone(); @@ -259,7 +264,16 @@ var OrbitControls = function ( object, domElement ) { var startEvent = { type: 'start' }; var endEvent = { type: 'end' }; - var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; + var STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; var state = STATE.NONE; @@ -590,26 +604,34 @@ var OrbitControls = function ( object, domElement ) { //console.log( 'handleTouchStartRotate' ); - rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + if ( event.touches.length == 1 ) { - } + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - function handleTouchStartDollyPan( event ) { + } - //console.log( 'handleTouchStartDollyPan' ); + else { - if ( scope.enableZoom ) { + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + rotateStart.set( x, y ); - 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 ); + function handleTouchStartPan( event ) { - dollyStart.set( 0, distance ); + //console.log( 'handleTouchStartPan' ); + + if ( event.touches.length == 1 ) { + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); } - if ( scope.enablePan ) { + else { var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); @@ -620,11 +642,57 @@ var OrbitControls = function ( object, domElement ) { } + function handleTouchStartDolly( event ) { + + //console.log( 'handleTouchStartDolly' ); + + 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 ); + + } + + function handleTouchStartDollyPan( event ) { + + //console.log( 'handleTouchStartDollyPan' ); + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enablePan ) handleTouchStartPan( event ); + + } + + function handleTouchStartDollyRotate( event ) { + + //console.log( 'handleTouchStartDollyRotate' ); + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enableRotate ) handleTouchStartRotate( event ); + + } + function handleTouchMoveRotate( event ) { //console.log( 'handleTouchMoveRotate' ); - rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + if ( event.touches.length == 1 ) { + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + else { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + rotateEnd.set( x, y ); + + } rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); @@ -636,47 +704,71 @@ var OrbitControls = function ( object, domElement ) { rotateStart.copy( rotateEnd ); - scope.update(); + } + + function handleTouchMovePan( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + if ( event.touches.length == 1 ) { + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + else { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); } - function handleTouchMoveDollyPan( event ) { + function handleTouchMoveDolly( event ) { - //console.log( 'handleTouchMoveDollyPan' ); + //console.log( 'handleTouchMoveRotate' ); - if ( scope.enableZoom ) { + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - 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 ); - var distance = Math.sqrt( dx * dx + dy * dy ); + dollyEnd.set( 0, distance ); - dollyEnd.set( 0, distance ); + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); - dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + dollyIn( dollyDelta.y ); - dollyIn( dollyDelta.y ); + dollyStart.copy( dollyEnd ); - dollyStart.copy( dollyEnd ); + } - } + function handleTouchMoveDollyPan( event ) { - if ( scope.enablePan ) { + //console.log( 'handleTouchMoveDollyPan' ); - var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); - var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + if ( scope.enableZoom ) handleTouchMoveDolly( event ); - panEnd.set( x, y ); + if ( scope.enablePan ) handleTouchMovePan( event ); - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + } - pan( panDelta.x, panDelta.y ); + function handleTouchMoveDollyRotate( event ) { - panStart.copy( panEnd ); + //console.log( 'handleTouchMoveDollyPan' ); - } + if ( scope.enableZoom ) handleTouchMoveDolly( event ); - scope.update(); + if ( scope.enableRotate ) handleTouchMoveRotate( event ); } @@ -705,45 +797,115 @@ var OrbitControls = function ( object, domElement ) { switch ( event.button ) { - case scope.mouseButtons.LEFT: + case 0: + + switch ( scope.mouseButtons.LEFT ) { + + case MOUSE.ROTATE: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + } else { - if ( scope.enablePan === false ) return; + if ( scope.enableRotate === false ) return; - handleMouseDownPan( event ); + handleMouseDownRotate( event ); - state = STATE.PAN; + state = STATE.ROTATE; - } else { + } - if ( scope.enableRotate === false ) return; + break; - handleMouseDownRotate( event ); + case MOUSE.PAN: - state = STATE.ROTATE; + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + break; + + default: + + state = STATE.NONE; } break; - case scope.mouseButtons.MIDDLE: - if ( scope.enableZoom === false ) return; + case 1: + + switch ( scope.mouseButtons.MIDDLE ) { + + case MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; - handleMouseDownDolly( event ); + break; - state = STATE.DOLLY; + + default: + + state = STATE.NONE; + + } break; - case scope.mouseButtons.RIGHT: + case 2: - if ( scope.enablePan === false ) return; + switch ( scope.mouseButtons.RIGHT ) { - handleMouseDownPan( event ); + case MOUSE.ROTATE: - state = STATE.PAN; + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + break; + + case MOUSE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + break; + + default: + + state = STATE.NONE; + + } break; @@ -842,23 +1004,67 @@ var OrbitControls = function ( object, domElement ) { switch ( event.touches.length ) { - case 1: // one-fingered touch: rotate + case 1: - if ( scope.enableRotate === false ) return; + switch ( scope.touches.ONE ) { + + case TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan( event ); + + state = STATE.TOUCH_PAN; - handleTouchStartRotate( event ); + break; - state = STATE.TOUCH_ROTATE; + default: + + state = STATE.NONE; + + } break; - case 2: // two-fingered touch: dolly-pan + case 2: - if ( scope.enableZoom === false && scope.enablePan === false ) return; + switch ( scope.touches.TWO ) { + + case TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; - handleTouchStartDollyPan( event ); + handleTouchStartDollyRotate( event ); - state = STATE.TOUCH_DOLLY_PAN; + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } break; @@ -883,24 +1089,46 @@ var OrbitControls = function ( object, domElement ) { event.preventDefault(); event.stopPropagation(); - switch ( event.touches.length ) { + switch ( state ) { - case 1: // one-fingered touch: rotate + case STATE.TOUCH_ROTATE: if ( scope.enableRotate === false ) return; - if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? handleTouchMoveRotate( event ); + scope.update(); + + break; + + case STATE.TOUCH_PAN: + + if ( scope.enablePan === false ) return; + + handleTouchMovePan( event ); + + scope.update(); + break; - case 2: // two-fingered touch: dolly-pan + case STATE.TOUCH_DOLLY_PAN: if ( scope.enableZoom === false && scope.enablePan === false ) return; - if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? handleTouchMoveDollyPan( event ); + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchMoveDollyRotate( event ); + + scope.update(); + break; default: @@ -1078,4 +1306,27 @@ Object.defineProperties( OrbitControls.prototype, { } ); -export { OrbitControls }; +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// This is very similar to OrbitControls, another set of touch behavior +// +// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - left mouse, or arrow keys / touch: one-finger move + +var MapControls = function ( object, domElement ) { + + OrbitControls.call( this, object, domElement ); + + this.mouseButtons.LEFT = MOUSE.PAN; + this.mouseButtons.RIGHT = MOUSE.ROTATE; + + this.touches.ONE = TOUCH.PAN; + this.touches.TWO = TOUCH.DOLLY_ROTATE; + +} + +MapControls.prototype = Object.create( EventDispatcher.prototype ); +MapControls.prototype.constructor = MapControls; + +export { OrbitControls, MapControls }; diff --git a/examples/misc_controls_map.html b/examples/misc_controls_map.html index 843cf1a4960b49..06014ff215db28 100644 --- a/examples/misc_controls_map.html +++ b/examples/misc_controls_map.html @@ -28,7 +28,7 @@ import { GUI } from './jsm/libs/dat.gui.module.js'; - import { MapControls } from './jsm/controls/MapControls.js'; + import { MapControls } from './jsm/controls/OrbitControls.js'; var camera, controls, scene, renderer; diff --git a/src/constants.d.ts b/src/constants.d.ts index e43f6f3b491a80..acc5d083ed1a0d 100644 --- a/src/constants.d.ts +++ b/src/constants.d.ts @@ -5,6 +5,16 @@ export enum MOUSE { LEFT, MIDDLE, RIGHT, + ROTATE, + DOLLY, + PAN, +} + +export enum TOUCH { + ROTATE, + PAN, + DOLLY_PAN, + DOLLY_ROTATE, } // GL STATE CONSTANTS diff --git a/src/constants.js b/src/constants.js index 6a294274536f4f..8dc6d9572a2a81 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,5 +1,6 @@ export var REVISION = '107dev'; -export var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; +export var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; +export var TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; export var CullFaceNone = 0; export var CullFaceBack = 1; export var CullFaceFront = 2; diff --git a/test/unit/src/constants.tests.js b/test/unit/src/constants.tests.js index 5746232dcb2db1..14306de8b17d31 100644 --- a/test/unit/src/constants.tests.js +++ b/test/unit/src/constants.tests.js @@ -9,7 +9,8 @@ export default QUnit.module( 'Constants', () => { QUnit.test( "default values", ( assert ) => { - assert.propEqual( Constants.MOUSE, { LEFT: 0, MIDDLE: 1, RIGHT: 2 }, 'MOUSE equal { LEFT: 0, MIDDLE: 1, RIGHT: 2 }' ); + assert.propEqual( Constants.MOUSE, { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }, 'MOUSE equal { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }' ); + assert.propEqual( Constants.TOUCH, { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }, 'TOUCH equal { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }' ); assert.equal( Constants.CullFaceNone, 0, 'CullFaceNone equal 0' ); assert.equal( Constants.CullFaceBack, 1, 'CullFaceBack equal 1' ); assert.equal( Constants.CullFaceFront, 2, 'CullFaceFront is equal to 2' ); diff --git a/utils/modularize.js b/utils/modularize.js index cb496fb67ad79c..63d38996c4d01a 100644 --- a/utils/modularize.js +++ b/utils/modularize.js @@ -24,7 +24,6 @@ var files = [ { path: 'controls/FirstPersonControls.js', dependencies: [], ignoreList: [] }, { path: 'controls/FlyControls.js', dependencies: [], ignoreList: [] }, { path: 'controls/OrbitControls.js', dependencies: [], ignoreList: [] }, - { path: 'controls/MapControls.js', dependencies: [], ignoreList: [] }, { path: 'controls/OrthographicTrackballControls.js', dependencies: [], ignoreList: [] }, { path: 'controls/PointerLockControls.js', dependencies: [], ignoreList: [] }, { path: 'controls/TrackballControls.js', dependencies: [], ignoreList: [] },