#include "ofQuaternion.h"
#include "ofMatrix4x4.h"
#include "ofMath.h"
#include "ofMathConstants.h"
#include "glm/gtc/quaternion.hpp"
ofQuaternion::ofQuaternion(const glm::quat & q):_v(q.x, q.y, q.z, q.w){}
ofQuaternion::operator glm::quat() const{
return glm::quat(_v.w, glm::vec3(_v.x, _v.y, _v.z));
}
void ofQuaternion::set(const ofMatrix4x4& matrix) {
*this = matrix.getRotate();
}
void ofQuaternion::get(ofMatrix4x4& matrix) const {
matrix.makeRotationMatrix(*this);
}
void ofQuaternion::makeRotate( float angle, float x, float y, float z ) {
angle = ofDegToRad(angle);
const float epsilon = 0.0000001f;
float length = sqrtf( x * x + y * y + z * z );
if (length < epsilon) {
*this = ofQuaternion();
return;
}
float inversenorm = 1.0f / length;
float coshalfangle = cosf( 0.5f * angle );
float sinhalfangle = sinf( 0.5f * angle );
_v.x = x * sinhalfangle * inversenorm;
_v.y = y * sinhalfangle * inversenorm;
_v.z = z * sinhalfangle * inversenorm;
_v.w = coshalfangle;
}
void ofQuaternion::makeRotate( float angle, const ofVec3f& vec ) {
makeRotate( angle, vec.x, vec.y, vec.z );
}
void ofQuaternion::makeRotate ( float angle1, const ofVec3f& axis1,
float angle2, const ofVec3f& axis2,
float angle3, const ofVec3f& axis3) {
ofQuaternion q1; q1.makeRotate(angle1,axis1);
ofQuaternion q2; q2.makeRotate(angle2,axis2);
ofQuaternion q3; q3.makeRotate(angle3,axis3);
*this = q1*q2*q3;
}
void ofQuaternion::makeRotate( const ofVec3f& from, const ofVec3f& to ) {
ofVec3f sourceVector = from;
ofVec3f targetVector = to;
float fromLen2 = from.lengthSquared();
float fromLen;
if ((fromLen2 < 1.0 - 1e-7) || (fromLen2 > 1.0 + 1e-7)) {
fromLen = sqrt(fromLen2);
sourceVector /= fromLen;
} else fromLen = 1.0;
float toLen2 = to.lengthSquared();
if ((toLen2 < 1.0 - 1e-7) || (toLen2 > 1.0 + 1e-7)) {
float toLen;
if ((toLen2 > fromLen2 - 1e-7) && (toLen2 < fromLen2 + 1e-7)) {
toLen = fromLen;
} else toLen = sqrt(toLen2);
targetVector /= toLen;
}
double dotProdPlus1 = 1.0 + sourceVector.dot(targetVector);
if (dotProdPlus1 < 1e-7) {
if (fabs(sourceVector.x) < 0.6) {
const double norm = sqrt(1.0 - sourceVector.x * sourceVector.x);
_v.x = 0.0;
_v.y = sourceVector.z / norm;
_v.z = -sourceVector.y / norm;
_v.w = 0.0;
} else if (fabs(sourceVector.y) < 0.6) {
const double norm = sqrt(1.0 - sourceVector.y * sourceVector.y);
_v.x = -sourceVector.z / norm;
_v.y = 0.0;
_v.z = sourceVector.x / norm;
_v.w = 0.0;
} else {
const double norm = sqrt(1.0 - sourceVector.z * sourceVector.z);
_v.x = sourceVector.y / norm;
_v.y = -sourceVector.x / norm;
_v.z = 0.0;
_v.w = 0.0;
}
}
else {
const double s = sqrt(0.5 * dotProdPlus1);
const ofVec3f tmp = sourceVector.getCrossed(targetVector) / (2.0 * s);
_v.x = tmp.x;
_v.y = tmp.y;
_v.z = tmp.z;
_v.w = s;
}
}
void ofQuaternion::makeRotate_original( const ofVec3f& from, const ofVec3f& to ) {
const float epsilon = 0.0000001f;
float length1 = from.length();
float length2 = to.length();
float cosangle = from.dot(to) / (length1 * length2);
if ( fabs(cosangle - 1) < epsilon ) {
makeRotate( 0.0, 0.0, 0.0, 1.0 );
} else
if ( fabs(cosangle + 1.0) < epsilon ) {
ofVec3f tmp;
if (fabs(from.x) < fabs(from.y))
if (fabs(from.x) < fabs(from.z)) tmp.set(1.0, 0.0, 0.0);
else tmp.set(0.0, 0.0, 1.0);
else if (fabs(from.y) < fabs(from.z)) tmp.set(0.0, 1.0, 0.0);
else tmp.set(0.0, 0.0, 1.0);
ofVec3f fromd(from.x, from.y, from.z);
ofVec3f axis(fromd.getCrossed(tmp));
axis.normalize();
_v.x = axis[0];
_v.y = axis[1];
_v.z = axis[2];
_v.w = 0;
} else {
ofVec3f axis(from.getCrossed(to));
float angle = acos( cosangle );
makeRotate( angle, axis );
}
}
void ofQuaternion::getRotate( float& angle, ofVec3f& vec ) const {
float x, y, z;
getRotate(angle, x, y, z);
vec.x = x;
vec.y = y;
vec.z = z;
}
void ofQuaternion::getRotate( float& angle, float& x, float& y, float& z ) const {
float sinhalfangle = sqrt( _v.x * _v.x + _v.y * _v.y + _v.z * _v.z );
angle = 2.0 * atan2( sinhalfangle, _v.w );
if (sinhalfangle) {
x = _v.x / sinhalfangle;
y = _v.y / sinhalfangle;
z = _v.z / sinhalfangle;
} else {
x = 0.0;
y = 0.0;
z = 1.0;
}
angle = ofRadToDeg(angle);
}
void ofQuaternion::slerp( float t, const ofQuaternion& from, const ofQuaternion& to ) {
const double epsilon = 0.00001;
double omega, cosomega, sinomega, scale_from, scale_to ;
ofQuaternion quatTo(to);
cosomega = from.asVec4().dot(to.asVec4());
if ( cosomega < 0.0 ) {
cosomega = -cosomega;
quatTo = -to;
}
if ( (1.0 - cosomega) > epsilon ) {
omega = acos(cosomega) ;
sinomega = sin(omega) ;
scale_from = sin((1.0 - t) * omega) / sinomega ;
scale_to = sin(t * omega) / sinomega ;
} else {
scale_from = 1.0 - t ;
scale_to = t ;
}
*this = (from * scale_from) + (quatTo * scale_to);
}
ofVec3f ofQuaternion::getEuler() const {
float test = x()*y() + z()*w();
float heading;
float attitude;
float bank;
if (test > 0.499) {
heading = 2 * atan2(x(), w());
attitude = PI/2;
bank = 0;
} else if (test < -0.499) {
heading = -2 * atan2(x(), w());
attitude = - PI/2;
bank = 0;
} else {
float sqx = x() * x();
float sqy = y() * y();
float sqz = z() * z();
heading = atan2(2.0f * y() * w() - 2.0f * x() * z(), 1.0f - 2.0f*sqy - 2.0f*sqz);
attitude = asin(2*test);
bank = atan2(2.0f*x() * w() - 2.0f * y() * z(), 1.0f - 2.0f*sqx - 2.0f*sqz);
}
return ofVec3f(ofRadToDeg(bank), ofRadToDeg(heading), ofRadToDeg(attitude));
}
#define QX _v.x
#define QY _v.y
#define QZ _v.z
#define QW _v.w
std::ostream& operator<<(std::ostream& os, const ofQuaternion &q) {
os << q._v.x << ", " << q._v.y << ", " << q._v.z << ", " << q._v.w;
return os;
}
std::istream& operator>>(std::istream& is, ofQuaternion &q) {
is >> q._v.x;
is.ignore(2);
is >> q._v.y;
is.ignore(2);
is >> q._v.z;
is.ignore(2);
is >> q._v.w;
return is;
}
Comments