( function () {
/**
* Little Planet
* @constructor
* @param {string} type - Type of little planet basic class
* @param {string} source - URL for the image source
* @param {number} [size=10000] - Size of plane geometry
* @param {number} [ratio=0.5] - Ratio of plane geometry's height against width
*/
PANOLENS.LittlePlanet = function ( type, source, size, ratio ) {
type = type || 'image';
type === 'image' && PANOLENS.ImagePanorama.call( this, source, size );
this.size = size || 10000;
this.ratio = ratio || 0.5;
this.EPS = 0.000001;
this.frameId;
this.geometry = this.createGeometry();
this.material = this.createMaterial( this.size );
this.dragging = false;
this.userMouse = new THREE.Vector2();
this.quatA = new THREE.Quaternion();
this.quatB = new THREE.Quaternion();
this.quatCur = new THREE.Quaternion();
this.quatSlerp = new THREE.Quaternion();
this.vectorX = new THREE.Vector3( 1, 0, 0 );
this.vectorY = new THREE.Vector3( 0, 1, 0 );
this.addEventListener( 'window-resize', this.onWindowResize );
};
PANOLENS.LittlePlanet.prototype = Object.create( PANOLENS.ImagePanorama.prototype );
PANOLENS.LittlePlanet.prototype.constructor = PANOLENS.LittlePlanet;
PANOLENS.LittlePlanet.prototype.createGeometry = function () {
return new THREE.PlaneGeometry( this.size, this.size * this.ratio );
};
PANOLENS.LittlePlanet.prototype.createMaterial = function ( size ) {
var uniforms = PANOLENS.StereographicShader.uniforms;
uniforms.zoom.value = size;
return new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: PANOLENS.StereographicShader.vertexShader,
fragmentShader: PANOLENS.StereographicShader.fragmentShader
} );
};
PANOLENS.LittlePlanet.prototype.registerMouseEvents = function () {
this.container.addEventListener( 'mousedown', this.onMouseDown.bind( this ), false );
this.container.addEventListener( 'mousemove', this.onMouseMove.bind( this ), false );
this.container.addEventListener( 'mouseup', this.onMouseUp.bind( this ), false );
this.container.addEventListener( 'touchstart', this.onMouseDown.bind( this ), false );
this.container.addEventListener( 'touchmove', this.onMouseMove.bind( this ), false );
this.container.addEventListener( 'touchend', this.onMouseUp.bind( this ), false );
this.container.addEventListener( 'mousewheel', this.onMouseWheel.bind( this ), false );
this.container.addEventListener( 'DOMMouseScroll', this.onMouseWheel.bind( this ), false );
this.container.addEventListener( 'contextmenu', this.onContextMenu.bind( this ), false );
};
PANOLENS.LittlePlanet.prototype.unregisterMouseEvents = function () {
this.container.removeEventListener( 'mousedown', this.onMouseDown.bind( this ), false );
this.container.removeEventListener( 'mousemove', this.onMouseMove.bind( this ), false );
this.container.removeEventListener( 'mouseup', this.onMouseUp.bind( this ), false );
this.container.removeEventListener( 'touchstart', this.onMouseDown.bind( this ), false );
this.container.removeEventListener( 'touchmove', this.onMouseMove.bind( this ), false );
this.container.removeEventListener( 'touchend', this.onMouseUp.bind( this ), false );
this.container.removeEventListener( 'mousewheel', this.onMouseWheel.bind( this ), false );
this.container.removeEventListener( 'DOMMouseScroll', this.onMouseWheel.bind( this ), false );
this.container.removeEventListener( 'contextmenu', this.onContextMenu.bind( this ), false );
};
PANOLENS.LittlePlanet.prototype.onMouseDown = function ( event ) {
var x = ( event.clientX >= 0 ) ? event.clientX : event.touches[ 0 ].clientX;
var y = ( event.clientY >= 0 ) ? event.clientY : event.touches[ 0 ].clientY;
var inputCount = ( event.touches && event.touches.length ) || 1 ;
switch ( inputCount ) {
case 1:
this.dragging = true;
this.userMouse.set( x, y );
break;
case 2:
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 );
this.userMouse.pinchDistance = distance;
break;
default:
break;
}
this.onUpdateCallback();
};
PANOLENS.LittlePlanet.prototype.onMouseMove = function ( event ) {
var x = ( event.clientX >= 0 ) ? event.clientX : event.touches[ 0 ].clientX;
var y = ( event.clientY >= 0 ) ? event.clientY : event.touches[ 0 ].clientY;
var inputCount = ( event.touches && event.touches.length ) || 1 ;
switch ( inputCount ) {
case 1:
var angleX = THREE.Math.degToRad( x - this.userMouse.x ) * 0.4;
var angleY = THREE.Math.degToRad( y - this.userMouse.y ) * 0.4;
if ( this.dragging ) {
this.quatA.setFromAxisAngle( this.vectorY, angleX );
this.quatB.setFromAxisAngle( this.vectorX, angleY );
this.quatCur.multiply( this.quatA ).multiply( this.quatB );
this.userMouse.set( x, y );
}
break;
case 2:
var uniforms = this.material.uniforms;
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 );
this.addZoomDelta( this.userMouse.pinchDistance - distance );
break;
default:
break;
}
};
PANOLENS.LittlePlanet.prototype.onMouseUp = function ( event ) {
this.dragging = false;
};
PANOLENS.LittlePlanet.prototype.onMouseWheel = function ( event ) {
event.preventDefault();
event.stopPropagation();
var delta = 0;
if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail !== undefined ) { // Firefox
delta = - event.detail;
}
this.addZoomDelta( delta );
this.onUpdateCallback();
};
PANOLENS.LittlePlanet.prototype.addZoomDelta = function ( delta ) {
var uniforms = this.material.uniforms;
var lowerBound = this.size * 0.1;
var upperBound = this.size * 10;
uniforms.zoom.value += delta;
if ( uniforms.zoom.value <= lowerBound ) {
uniforms.zoom.value = lowerBound;
} else if ( uniforms.zoom.value >= upperBound ) {
uniforms.zoom.value = upperBound;
}
};
PANOLENS.LittlePlanet.prototype.onUpdateCallback = function () {
this.frameId = window.requestAnimationFrame( this.onUpdateCallback.bind( this ) );
this.quatSlerp.slerp( this.quatCur, 0.1 );
this.material.uniforms.transform.value.makeRotationFromQuaternion( this.quatSlerp );
if ( !this.dragging && 1.0 - this.quatSlerp.clone().dot( this.quatCur ) < this.EPS ) {
window.cancelAnimationFrame( this.frameId );
}
};
PANOLENS.LittlePlanet.prototype.reset = function () {
this.quatCur.set( 0, 0, 0, 1 );
this.quatSlerp.set( 0, 0, 0, 1 );
this.onUpdateCallback();
};
PANOLENS.LittlePlanet.prototype.onLoad = function () {
this.material.uniforms.resolution.value = this.container.clientWidth / this.container.clientHeight;
this.registerMouseEvents();
this.onUpdateCallback();
this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'disableControl' } );
};
PANOLENS.LittlePlanet.prototype.onLeave = function () {
this.unregisterMouseEvents();
this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'enableControl', data: PANOLENS.Controls.ORBIT } );
window.cancelAnimationFrame( this.frameId );
PANOLENS.Panorama.prototype.onLeave.call( this );
};
PANOLENS.LittlePlanet.prototype.onWindowResize = function () {
this.material.uniforms.resolution.value = this.container.clientWidth / this.container.clientHeight;
};
PANOLENS.LittlePlanet.prototype.onContextMenu = function () {
this.dragging = false;
};
})();