New repo for repo.or.cz
[The-Artvertiser.git] / artvertiser / MatrixTracker / ofxQuaternion.cpp
blob5be781e9ddbb08a79e6f684a1a4bc8b7625f729b
1 /*
2 Copyright 2009 openFrameworks
4 Distributed under the terms of the GNU General Public License v3.
6 This file is part of The Artvertiser.
8 The Artvertiser is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 The Artvertiser is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with The Artvertiser. If not, see <http://www.gnu.org/licenses/>.
22 #include <stdio.h>
23 #include "ofxQuaternion.h"
24 #include "ofxMatrix4x4.h"
26 void ofxQuaternion::set(const ofxMatrix4x4& matrix) {
27 *this = matrix.getRotate();
30 void ofxQuaternion::get(ofxMatrix4x4& matrix) const {
31 matrix.makeRotationMatrix(*this);
35 /// Set the elements of the Quat to represent a rotation of angle
36 /// (radians) around the axis (x,y,z)
37 void ofxQuaternion::makeRotate( float angle, float x, float y, float z ) {
38 const float epsilon = 0.0000001f;
40 float length = sqrtf( x * x + y * y + z * z );
41 if (length < epsilon) {
42 // ~zero length axis, so reset rotation to zero.
43 *this = ofxQuaternion();
44 return;
47 float inversenorm = 1.0f / length;
48 float coshalfangle = cosf( 0.5f * angle );
49 float sinhalfangle = sinf( 0.5f * angle );
51 _v[0] = x * sinhalfangle * inversenorm;
52 _v[1] = y * sinhalfangle * inversenorm;
53 _v[2] = z * sinhalfangle * inversenorm;
54 _v[3] = coshalfangle;
58 void ofxQuaternion::makeRotate( float angle, const ofxVec3f& vec ) {
59 makeRotate( angle, vec.x, vec.y, vec.z );
63 void ofxQuaternion::makeRotate ( float angle1, const ofxVec3f& axis1,
64 float angle2, const ofxVec3f& axis2,
65 float angle3, const ofxVec3f& axis3) {
66 ofxQuaternion q1; q1.makeRotate(angle1,axis1);
67 ofxQuaternion q2; q2.makeRotate(angle2,axis2);
68 ofxQuaternion q3; q3.makeRotate(angle3,axis3);
70 *this = q1*q2*q3;
73 /** Make a rotation Quat which will rotate vec1 to vec2
75 This routine uses only fast geometric transforms, without costly acos/sin computations.
76 It's exact, fast, and with less degenerate cases than the acos/sin method.
78 For an explanation of the math used, you may see for example:
79 http://logiciels.cnes.fr/MARMOTTES/marmottes-mathematique.pdf
81 @note This is the rotation with shortest angle, which is the one equivalent to the
82 acos/sin transform method. Other rotations exists, for example to additionally keep
83 a local horizontal attitude.
85 @author Nicolas Brodu
87 void ofxQuaternion::makeRotate( const ofxVec3f& from, const ofxVec3f& to ) {
89 // This routine takes any vector as argument but normalized
90 // vectors are necessary, if only for computing the dot product.
91 // Too bad the API is that generic, it leads to performance loss.
92 // Even in the case the 2 vectors are not normalized but same length,
93 // the sqrt could be shared, but we have no way to know beforehand
94 // at this point, while the caller may know.
95 // So, we have to test... in the hope of saving at least a sqrt
96 ofxVec3f sourceVector = from;
97 ofxVec3f targetVector = to;
99 float fromLen2 = from.lengthSquared();
100 float fromLen;
101 // normalize only when necessary, epsilon test
102 if ((fromLen2 < 1.0 - 1e-7) || (fromLen2 > 1.0 + 1e-7)) {
103 fromLen = sqrt(fromLen2);
104 sourceVector /= fromLen;
105 } else fromLen = 1.0;
107 float toLen2 = to.lengthSquared();
108 // normalize only when necessary, epsilon test
109 if ((toLen2 < 1.0 - 1e-7) || (toLen2 > 1.0 + 1e-7)) {
110 float toLen;
111 // re-use fromLen for case of mapping 2 vectors of the same length
112 if ((toLen2 > fromLen2 - 1e-7) && (toLen2 < fromLen2 + 1e-7)) {
113 toLen = fromLen;
114 } else toLen = sqrt(toLen2);
115 targetVector /= toLen;
119 // Now let's get into the real stuff
120 // Use "dot product plus one" as test as it can be re-used later on
121 double dotProdPlus1 = 1.0 + sourceVector.dot(targetVector);
123 // Check for degenerate case of full u-turn. Use epsilon for detection
124 if (dotProdPlus1 < 1e-7) {
126 // Get an orthogonal vector of the given vector
127 // in a plane with maximum vector coordinates.
128 // Then use it as quaternion axis with pi angle
129 // Trick is to realize one value at least is >0.6 for a normalized vector.
130 if (fabs(sourceVector.x) < 0.6) {
131 const double norm = sqrt(1.0 - sourceVector.x * sourceVector.x);
132 _v[0] = 0.0;
133 _v[1] = sourceVector.z / norm;
134 _v[2] = -sourceVector.y / norm;
135 _v[3] = 0.0;
136 } else if (fabs(sourceVector.y) < 0.6) {
137 const double norm = sqrt(1.0 - sourceVector.y * sourceVector.y);
138 _v[0] = -sourceVector.z / norm;
139 _v[1] = 0.0;
140 _v[2] = sourceVector.x / norm;
141 _v[3] = 0.0;
142 } else {
143 const double norm = sqrt(1.0 - sourceVector.z * sourceVector.z);
144 _v[0] = sourceVector.y / norm;
145 _v[1] = -sourceVector.x / norm;
146 _v[2] = 0.0;
147 _v[3] = 0.0;
151 else {
152 // Find the shortest angle quaternion that transforms normalized vectors
153 // into one other. Formula is still valid when vectors are colinear
154 const double s = sqrt(0.5 * dotProdPlus1);
155 const ofxVec3f tmp = sourceVector.getCrossed(targetVector) / (2.0 * s);
156 _v[0] = tmp.x;
157 _v[1] = tmp.y;
158 _v[2] = tmp.z;
159 _v[3] = s;
164 // Make a rotation Quat which will rotate vec1 to vec2
165 // Generally take adot product to get the angle between these
166 // and then use a cross product to get the rotation axis
167 // Watch out for the two special cases of when the vectors
168 // are co-incident or opposite in direction.
169 void ofxQuaternion::makeRotate_original( const ofxVec3f& from, const ofxVec3f& to ) {
170 const float epsilon = 0.0000001f;
172 float length1 = from.length();
173 float length2 = to.length();
175 // dot product vec1*vec2
176 float cosangle = from.dot(to) / (length1 * length2);
178 if ( fabs(cosangle - 1) < epsilon ) {
179 //osg::notify(osg::INFO)<<"*** Quat::makeRotate(from,to) with near co-linear vectors, epsilon= "<<fabs(cosangle-1)<<std::endl;
181 // cosangle is close to 1, so the vectors are close to being coincident
182 // Need to generate an angle of zero with any vector we like
183 // We'll choose (1,0,0)
184 makeRotate( 0.0, 0.0, 0.0, 1.0 );
185 } else
186 if ( fabs(cosangle + 1.0) < epsilon ) {
187 // vectors are close to being opposite, so will need to find a
188 // vector orthongonal to from to rotate about.
189 ofxVec3f tmp;
190 if (fabs(from.x) < fabs(from.y))
191 if (fabs(from.x) < fabs(from.z)) tmp.set(1.0, 0.0, 0.0); // use x axis.
192 else tmp.set(0.0, 0.0, 1.0);
193 else if (fabs(from.y) < fabs(from.z)) tmp.set(0.0, 1.0, 0.0);
194 else tmp.set(0.0, 0.0, 1.0);
196 ofxVec3f fromd(from.x, from.y, from.z);
198 // find orthogonal axis.
199 ofxVec3f axis(fromd.getCrossed(tmp));
200 axis.normalize();
202 _v[0] = axis[0]; // sin of half angle of PI is 1.0.
203 _v[1] = axis[1]; // sin of half angle of PI is 1.0.
204 _v[2] = axis[2]; // sin of half angle of PI is 1.0.
205 _v[3] = 0; // cos of half angle of PI is zero.
207 } else {
208 // This is the usual situation - take a cross-product of vec1 and vec2
209 // and that is the axis around which to rotate.
210 ofxVec3f axis(from.getCrossed(to));
211 float angle = acos( cosangle );
212 makeRotate( angle, axis );
216 void ofxQuaternion::getRotate( float& angle, ofxVec3f& vec ) const {
217 float x, y, z;
218 getRotate(angle, x, y, z);
219 vec[0] = x;
220 vec[1] = y;
221 vec[2] = z;
223 // Get the angle of rotation and axis of this Quat object.
224 // Won't give very meaningful results if the Quat is not associated
225 // with a rotation!
226 void ofxQuaternion::getRotate( float& angle, float& x, float& y, float& z ) const {
227 float sinhalfangle = sqrt( _v[0] * _v[0] + _v[1] * _v[1] + _v[2] * _v[2] );
229 angle = 2.0 * atan2( sinhalfangle, _v[3] );
230 if (sinhalfangle) {
231 x = _v[0] / sinhalfangle;
232 y = _v[1] / sinhalfangle;
233 z = _v[2] / sinhalfangle;
234 } else {
235 x = 0.0;
236 y = 0.0;
237 z = 1.0;
243 /// Spherical Linear Interpolation
244 /// As t goes from 0 to 1, the Quat object goes from "from" to "to"
245 /// Reference: Shoemake at SIGGRAPH 89
246 /// See also
247 /// http://www.gamasutra.com/features/programming/19980703/quaternions_01.htm
248 void ofxQuaternion::slerp( float t, const ofxQuaternion& from, const ofxQuaternion& to ) {
249 const double epsilon = 0.00001;
250 double omega, cosomega, sinomega, scale_from, scale_to ;
252 ofxQuaternion quatTo(to);
253 // this is a dot product
255 cosomega = from.asVec4().dot(to.asVec4());
257 if ( cosomega < 0.0 ) {
258 cosomega = -cosomega;
259 quatTo = -to;
262 if ( (1.0 - cosomega) > epsilon ) {
263 omega = acos(cosomega) ; // 0 <= omega <= Pi (see man acos)
264 sinomega = sin(omega) ; // this sinomega should always be +ve so
265 // could try sinomega=sqrt(1-cosomega*cosomega) to avoid a sin()?
266 scale_from = sin((1.0 - t) * omega) / sinomega ;
267 scale_to = sin(t * omega) / sinomega ;
268 } else {
269 /* --------------------------------------------------
270 The ends of the vectors are very close
271 we can use simple linear interpolation - no need
272 to worry about the "spherical" interpolation
273 -------------------------------------------------- */
274 scale_from = 1.0 - t ;
275 scale_to = t ;
278 *this = (from * scale_from) + (quatTo * scale_to);
280 // so that we get a Vec4
284 #define QX _v[0]
285 #define QY _v[1]
286 #define QZ _v[2]
287 #define QW _v[3]