00001 /* 00002 Copyright (c) 2010, The Barbarian Group 00003 All rights reserved. 00004 00005 Redistribution and use in source and binary forms, with or without modification, are permitted provided that 00006 the following conditions are met: 00007 00008 * Redistributions of source code must retain the above copyright notice, this list of conditions and 00009 the following disclaimer. 00010 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 00011 the following disclaimer in the documentation and/or other materials provided with the distribution. 00012 00013 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 00014 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 00015 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 00016 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00017 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00018 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00019 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00020 POSSIBILITY OF SUCH DAMAGE. 00021 */ 00022 00023 #pragma once 00024 00025 #include "cinder/Vector.h" 00026 00027 namespace cinder { 00028 00029 class Arcball { 00030 public: 00031 Arcball() 00032 { 00033 setNoConstraintAxis(); 00034 mCurrentQuat = mInitialQuat = Quatf( Vec3f::yAxis(), 0 ); 00035 } 00036 Arcball( const Vec2i &aScreenSize ) 00037 : mWindowSize( aScreenSize ) 00038 { 00039 setCenter( Vec2f( mWindowSize.x / 2.0f, mWindowSize.y / 2.0f ) ); 00040 mRadius = std::min( (float)mWindowSize.x / 2, (float)mWindowSize.y / 2 ); 00041 setNoConstraintAxis(); 00042 mCurrentQuat = mInitialQuat = Quatf( Vec3f::yAxis(), 0 ); 00043 } 00044 00045 void mouseDown( const Vec2i &mousePos ) 00046 { 00047 mInitialMousePos = mousePos; 00048 mInitialQuat = mCurrentQuat; 00049 } 00050 00051 void mouseDrag( const Vec2i &mousePos ) 00052 { 00053 Vec3f from = mouseOnSphere( mInitialMousePos ); 00054 Vec3f to = mouseOnSphere( mousePos ); 00055 if( mUseConstraint ) { 00056 from = constrainToAxis( from, mConstraintAxis ); 00057 to = constrainToAxis( to, mConstraintAxis ); 00058 } 00059 00060 Vec3f axis = from.cross( to ); 00061 mCurrentQuat = mInitialQuat * Quatf( from.dot( to ), axis.x, axis.y, axis.z ); 00062 mCurrentQuat.normalize(); 00063 } 00064 00065 void resetQuat() { mCurrentQuat = mInitialQuat = Quatf( Vec3f::yAxis(), 0 ); } 00066 Quatf getQuat() { return mCurrentQuat; } 00067 void setQuat( const Quatf &quat ) { mCurrentQuat = quat; } 00068 00069 void setWindowSize( const Vec2i &aWindowSize ) { mWindowSize = aWindowSize; } 00070 void setCenter( const Vec2f &aCenter ) { mCenter = aCenter; } 00071 Vec2f getCenter() const { return mCenter; } 00072 void setRadius( float aRadius ) { mRadius = aRadius; } 00073 float getRadius() const { return mRadius; } 00074 void setConstraintAxis( const Vec3f &aConstraintAxis ) { mConstraintAxis = aConstraintAxis; mUseConstraint = true; } 00075 void setNoConstraintAxis() { mUseConstraint = false; } 00076 bool isUsingConstraint() const { return mUseConstraint; } 00077 Vec3f getConstraintAxis() const { return mConstraintAxis; } 00078 00079 Vec3f mouseOnSphere( const Vec2i &point ) { 00080 Vec3f result; 00081 00082 result.x = ( point.x - mCenter.x ) / ( mRadius * 2 ); 00083 result.y = ( point.y - mCenter.y ) / ( mRadius * 2 ); 00084 result.z = 0.0f; 00085 00086 float mag = result.lengthSquared(); 00087 if( mag > 1.0f ) { 00088 result.normalize(); 00089 } 00090 else { 00091 result.z = math<float>::sqrt( 1.0f - mag ); 00092 result.normalize(); 00093 } 00094 00095 return result; 00096 } 00097 00098 private: 00099 // Force sphere point to be perpendicular to axis 00100 Vec3f constrainToAxis( const Vec3f &loose, const Vec3f &axis ) 00101 { 00102 float norm; 00103 Vec3f onPlane = loose - axis * axis.dot( loose ); 00104 norm = onPlane.lengthSquared(); 00105 if( norm > 0.0f ) { 00106 if( onPlane.z < 0.0f ) 00107 onPlane = -onPlane; 00108 return ( onPlane * ( 1.0f / math<float>::sqrt( norm ) ) ); 00109 } 00110 00111 if( axis.dot( Vec3f::zAxis() ) < 0.0001f ) { 00112 onPlane = Vec3f::xAxis(); 00113 } 00114 else { 00115 onPlane = Vec3f( -axis.y, axis.x, 0.0f ).normalized(); 00116 } 00117 00118 return onPlane; 00119 } 00120 00121 Vec2i mWindowSize; 00122 Vec2i mInitialMousePos; 00123 Vec2f mCenter; 00124 Quatf mCurrentQuat, mInitialQuat; 00125 float mRadius; 00126 Vec3f mConstraintAxis; 00127 bool mUseConstraint; 00128 }; 00129 00130 } // namespace cinder