#include <math.h>
#include <assert.h>
#include <maya/MIOStream.h>
#define COMPILE_OUTSIDE_MAYA
#include <AwMath.h>
#include <AwVector.h>
#include <AwMatrix.h>
#include <AwQuaternion.h>
const AwQuaternion AwQuaternion::identity;
AwQuaternion::AwQuaternion(const AwVector &a, const AwVector &b)
: w(1.0), x(0.0), y(0.0), z(0.0)
{
double factor = a.length() * b.length();
if (fabs(factor) > kFloatEpsilon) {
AwVector pivotVector;
double dot = a.dotProduct(b) / factor;
double theta = acos(clamp(dot, -1.0, 1.0));
pivotVector = a^b;
if (dot < 0.0 && pivotVector.length() < kFloatEpsilon) {
size_t dominantIndex = a.dominantAxis();
pivotVector[dominantIndex] = -a[(dominantIndex+1)%3];
pivotVector[(dominantIndex+1)%3] = a[dominantIndex];
pivotVector[(dominantIndex+2)%3] = 0.0;
}
setAxisAngle(pivotVector, theta);
}
}
AwQuaternion & AwQuaternion::operator=(const AwMatrix &tm)
{
double trace, s;
int i, j, k;
trace = tm.matrix[0][0] + tm.matrix[1][1] + tm.matrix[2][2];
if (trace > 0.0) {
s = sqrt(trace + 1.0);
w = s * 0.5;
assert(s > kExtendedEpsilon);
s = 0.5/s;
x = (tm.matrix[1][2] - tm.matrix[2][1]) * s;
y = (tm.matrix[2][0] - tm.matrix[0][2]) * s;
z = (tm.matrix[0][1] - tm.matrix[1][0]) * s;
} else {
i = 0;
if (tm.matrix[1][1] > tm.matrix[0][0])
i = 1;
if (tm.matrix[2][2] > tm.matrix[i][i])
i = 2;
j = (i+1)%3;
k = (j+1)%3;
s = sqrt(tm.matrix[i][i] - (tm.matrix[j][j] + tm.matrix[k][k]) + 1.0);
(*this)[i] = s * 0.5;
assert(s > kExtendedEpsilon);
s = 0.5 / s;
w = (tm.matrix[j][k] - tm.matrix[k][j]) * s;
(*this)[j] = (tm.matrix[j][i] + tm.matrix[i][j]) * s;
(*this)[k] = (tm.matrix[k][i] + tm.matrix[i][k]) * s;
}
return *this;
}
AwQuaternion AwQuaternion::operator*(const AwQuaternion &rhs) const
{
AwQuaternion result;
result.w = rhs.w * w - (rhs.x * x + rhs.y * y + rhs.z * z);
result.x = rhs.w * x + rhs.x * w + rhs.y * z - rhs.z * y;
result.y = rhs.w * y + rhs.y * w + rhs.z * x - rhs.x * z;
result.z = rhs.w * z + rhs.z * w + rhs.x * y - rhs.y * x;
return result;
}
AwQuaternion::operator AwMatrix() const
{
AwMatrix m;
convertToMatrix(m);
return m;
}
void AwQuaternion::convertToMatrix(AwMatrix &tm) const
{
double ww = w*w, xx = x*x, yy = y*y, zz = z*z;
double s = 2.0/(ww + xx + yy + zz);
double xy = x*y, xz = x*z, yz = y*z, wx = w*x, wy = w*y, wz = w*z;
tm.matrix[0][0] = 1.0 - s * (yy + zz);
tm.matrix[1][0] = s * (xy - wz);
tm.matrix[2][0] = s * (xz + wy);
tm.matrix[3][0] = 0.0;
tm.matrix[0][1] = s * (xy + wz);
tm.matrix[1][1] = 1.0 - s * (xx + zz);
tm.matrix[2][1] = s * (yz - wx);
tm.matrix[3][1] = 0.0;
tm.matrix[0][2] = s * (xz - wy);
tm.matrix[1][2] = s * (yz + wx);
tm.matrix[2][2] = 1.0 - s * (xx + yy);
tm.matrix[3][2] = 0.0;
tm.matrix[0][3] = 0.0;
tm.matrix[1][3] = 0.0;
tm.matrix[2][3] = 0.0;
tm.matrix[3][3] = 1.0;
}
AwQuaternion &AwQuaternion::setAxisAngle(const AwVector &axis, double theta)
{
double sumOfSquares =
(double) axis.x * axis.x +
(double) axis.y * axis.y +
(double) axis.z * axis.z;
if (sumOfSquares <= kDoubleEpsilon) {
*this = identity;
} else {
theta *= 0.5;
w = cos(theta);
double commonFactor = sin(theta);
if (!::equivalent(sumOfSquares, 1.0))
commonFactor /= sqrt(sumOfSquares);
x = commonFactor * (double) axis.x;
y = commonFactor * (double) axis.y;
z = commonFactor * (double) axis.z;
}
return *this;
}
bool AwQuaternion::getAxisAngle(AwVector &axis, double &theta) const
{
bool result;
double inverseOfSinThetaByTwo, thetaExtended;
if (::equivalent(w, (double) 1.0)) {
theta = 0.0;
if (axis.length() < kDoubleEpsilon) {
axis.set(0.0,0.0,1.0);
}
result = false;
}
else {
thetaExtended = acos(clamp(w,-1.0,1.0));
theta = thetaExtended * 2.0;
inverseOfSinThetaByTwo = 1.0 / sin(thetaExtended);
axis.x = x * inverseOfSinThetaByTwo;
axis.y = y * inverseOfSinThetaByTwo;
axis.z = z * inverseOfSinThetaByTwo;
result = true;
}
return result;
}
ostream &operator<<(ostream &os, const AwQuaternion &q)
{
os << "["
<< "x: " << q.x << ", "
<< "y: " << q.y << ", "
<< "z: " << q.z << ", "
<< "w: " << q.w << "]";
return os;
}