Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merging Ruijin's fix for issue #22 (rotate object not camera) #36

Merged
merged 3 commits into from
Apr 6, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion BezierView.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@

<!-- The library for reading patches -->
<script src="js/read.js"></script>


<!-- The library for reading patches -->
<script src="js/ObjectControls.js"></script>

<!-- The main renderer and starting point -->
<script src="js/render.js"></script>
Expand Down
350 changes: 350 additions & 0 deletions js/ObjectControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
/* Controller that move the object instead of the camera
*
* Modified from the TrackBallControls in Three.js
*
* Author: Ruijin Wu <cszwrj at gmail.com>
*/

THREE.ObjectControls = function ( object, camera, domElement ) {

var _this = this,
STATE = { NONE : -1, ROTATE : 0, ZOOM : 1, PAN : 2 };

this.object = object;
object.useQuaternion = true;

this.camera = camera;
this.domElement = ( domElement !== undefined ) ? domElement : document;

// API

this.enabled = true;

this.screen = { width: window.innerWidth, height: window.innerHeight, offsetLeft: 0, offsetTop: 0 };
this.radius = ( this.screen.width + this.screen.height ) / 4;

this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;

this.noRotate = false;
this.noZoom = false;
this.noPan = false;

this.staticMoving = false;
this.dynamicDampingFactor = 0.2;

this.minDistance = 0;
this.maxDistance = Infinity;

this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];

// internals

this.target = new THREE.Vector3( 0, 0, 0 );

var _keyPressed = false,
_state = STATE.NONE,

_eye = new THREE.Vector3(),

_rotateStart = new THREE.Vector3(),
_rotateEnd = new THREE.Vector3(),

_zoomStart = new THREE.Vector2(),
_zoomEnd = new THREE.Vector2(),

_panStart = new THREE.Vector2(),
_panEnd = new THREE.Vector2();


// methods

this.handleEvent = function ( event ) {

if ( typeof this[ event.type ] == 'function' ) {

this[ event.type ]( event );

}

};

this.getMouseOnScreen = function( clientX, clientY ) {

return new THREE.Vector2(
( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,
( clientY - _this.screen.offsetTop ) / _this.radius * 0.5
);

};

this.getMouseProjectionOnBall = function( clientX, clientY ) {

var mouseOnBall = new THREE.Vector3(
( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,
( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,
0.0
);

var length = mouseOnBall.length();

if ( length > 1.0 ) {
mouseOnBall.normalize();

} else {
mouseOnBall.z = Math.sqrt( 1.0 - length * length );

}

_eye.copy( _this.camera.position ).subSelf( _this.target );

var projection = _this.camera.up.clone().setLength( mouseOnBall.y );
projection.addSelf( _this.camera.up.clone().crossSelf( _eye ).setLength( mouseOnBall.x ) );
projection.addSelf( _eye.setLength( mouseOnBall.z ) );

return projection;

};

this.rotateObject = function() {

var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );

if ( angle ) {

var axis = ( new THREE.Vector3() ).cross( _rotateStart, _rotateEnd ).normalize(),
quaternion = new THREE.Quaternion();

angle *= _this.rotateSpeed;

var tQuaternion = new THREE.Quaternion();
tQuaternion.setFromAxisAngle( axis, angle );

// tQuaternion.normalize();
_this.object.quaternion.multiply(tQuaternion,_this.object.quaternion);
_this.object.quaternion.normalize();


quaternion.setFromAxisAngle( axis, -angle );


quaternion.multiplyVector3( _rotateEnd );

if ( _this.staticMoving ) {

_rotateStart = _rotateEnd;

} else {

quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
quaternion.multiplyVector3( _rotateStart );

}

}

};

this.zoomObject = function() {

var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;

if ( factor !== 1.0 && factor > 0.0 ) {

_this.object.scale.divideScalar(factor);

if ( _this.staticMoving ) {

_zoomStart = _zoomEnd;

} else {

_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;

}

}

};

this.panObject = function() {

var mouseChange = _panEnd.clone().subSelf( _panStart );

if ( mouseChange.lengthSq() ) {

mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );

var pan = _eye.clone().crossSelf( _this.object.up ).setLength( mouseChange.x );
pan.addSelf( _this.object.up.clone().setLength( mouseChange.y ) );

_this.object.position.subSelf( pan );

if ( _this.staticMoving ) {

_panStart = _panEnd;

} else {

_panStart.addSelf( mouseChange.sub( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );

}

}

};

this.update = function() {

if ( !_this.noRotate ) {

_this.rotateObject();

}

if ( !_this.noZoom ) {

_this.zoomObject();

}

if ( !_this.noPan ) {

_this.panObject();

}

_this.object.matrixWorldNeedsUpdate = true;

};


// listeners

function keydown( event ) {

if ( ! _this.enabled ) return;

if ( _state !== STATE.NONE ) {

return;

} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {

_state = STATE.ROTATE;

} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {

_state = STATE.ZOOM;

} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {

_state = STATE.PAN;

}

if ( _state !== STATE.NONE ) {

_keyPressed = true;

}

};

function keyup( event ) {

if ( ! _this.enabled ) return;

if ( _state !== STATE.NONE ) {

_state = STATE.NONE;

}

};

function mousedown( event ) {

if ( ! _this.enabled ) return;

event.preventDefault();
event.stopPropagation();

if ( _state === STATE.NONE ) {

_state = event.button;

if ( _state === STATE.ROTATE && !_this.noRotate ) {

_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );

} else if ( _state === STATE.ZOOM && !_this.noZoom ) {

_zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );

} else if ( !this.noPan ) {

_panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );

}

}

};

function mousemove( event ) {

if ( ! _this.enabled ) return;

if ( _keyPressed ) {

_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
_zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
_panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );

_keyPressed = false;

}

if ( _state === STATE.NONE ) {

return;

} else if ( _state === STATE.ROTATE && !_this.noRotate ) {

_rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );

} else if ( _state === STATE.ZOOM && !_this.noZoom ) {

_zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );

} else if ( _state === STATE.PAN && !_this.noPan ) {

_panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );

}

};

function mouseup( event ) {

if ( ! _this.enabled ) return;

event.preventDefault();
event.stopPropagation();

_state = STATE.NONE;

};

this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );

this.domElement.addEventListener( 'mousemove', mousemove, false );
this.domElement.addEventListener( 'mousedown', mousedown, false );
this.domElement.addEventListener( 'mouseup', mouseup, false );

window.addEventListener( 'keydown', keydown, false );
window.addEventListener( 'keyup', keyup, false );

};
Loading