/* PIP - Platform Independent Primitives Class for quaternions Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piquaternion.h" PIQuaternion::PIQuaternion(const PIMathVectorT3d & u, double a) { q[0] = a; q[1] = u[0]; q[2] = u[1]; q[3] = u[2]; } PIMathVectorT3d PIQuaternion::eyler() const { PIMathMatrixT33d rmat = rotationMatrix(); double angle_y = asin(rmat[0][2]), angle_x, angle_z; double c = cos(angle_y); if (fabs(c) < 1.E-5) { angle_x = 0; angle_z = atan2(rmat[1][0], rmat[1][1]); } else { angle_x = -atan2(-rmat[1][2] / c, rmat[2][2] / c); angle_z = atan2(-rmat[0][1] / c, rmat[0][0] / c); } if (angle_z < 0) angle_z = 2 * M_PI + angle_z; return PIMathVectorT3d({angle_x, angle_y, angle_z}); } PIMathMatrixT33d PIQuaternion::rotationMatrix() const { PIMathMatrixT33d ret; double xx = q[1] * q[1]; double xy = q[1] * q[2]; double xz = q[1] * q[3]; double xw = q[1] * q[0]; double yy = q[2] * q[2]; double yz = q[2] * q[3]; double yw = q[2] * q[0]; double zz = q[3] * q[3]; double zw = q[3] * q[0]; ret[0][0] = 1. - 2. * (yy + zz); ret[0][1] = 2. * (xy - zw); ret[0][2] = 2. * (xz + yw); ret[1][0] = 2. * (xy + zw); ret[1][1] = 1. - 2. * (xx + zz); ret[1][2] = 2. * (yz - xw); ret[2][0] = 2. * (xz - yw); ret[2][1] = 2. * (yz + xw); ret[2][2] = 1. - 2. * (xx + yy); return ret; } void PIQuaternion::axis(PIMathVectorT3d * ret) const { if (!ret) return; ret[0] = vector(); } PIQuaternion PIQuaternion::rotated(const PIMathVectorT3d & u, double a) const { PIQuaternion ret(*this); ret.rotate(u, a); return ret; } void PIQuaternion::rotate(const PIMathVectorT3d & u, double a) { PIQuaternion v(u * sin(a / 2.), cos(a / 2.)); *this = (v * (*this) * v.conjugate()); } void PIQuaternion::normalize() { double d = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); if (d != 0.) for (int i = 0; i < 4; ++i) q[i] /= d; } PIQuaternion PIQuaternion::fromEyler(double ax, double ay, double az) { PIQuaternion q_heading; PIQuaternion q_pinch; PIQuaternion q_bank; PIQuaternion q_tmp; q_heading.q[0] = 0; q_heading.q[1] = 0; q_heading.q[2] = sin(ax / 2); q_heading.q[3] = -cos(ax / 2); q_pinch.q[0] = 0; q_pinch.q[1] = sin(ay / 2); q_pinch.q[2] = 0; q_pinch.q[3] = -cos(ay / 2); az = M_PI - az; q_bank.q[0] = sin(az / 2); q_bank.q[1] = 0; q_bank.q[2] = 0; q_bank.q[3] = cos(az / 2); q_tmp = q_heading * q_pinch; q_tmp = q_tmp * q_bank; q_tmp.normalize(); return q_tmp; } PIQuaternion PIQuaternion::fromAngles(double ax, double ay, double az) { double c1 = cos(ay / 2); double s1 = sin(ay / 2); double c2 = cos(az / 2); double s2 = sin(az / 2); double c3 = cos(ax / 2); double s3 = sin(ax / 2); double c1c2 = c1 * c2; double s1s2 = s1 * s2; double a = c1c2 * c3 - s1s2 * s3; double x = c1c2 * s3 + s1s2 * c3; double y = s1 * c2 * c3 + c1 * s2 * s3; double z = c1 * s2 * c3 - s1 * c2 * s3; PIQuaternion res; res.q[0] = a; res.q[1] = x; res.q[2] = y; res.q[3] = z; return res; } PIQuaternion PIQuaternion::fromAngles2(double ax, double ay, double az) { double om = PIMathVectorT3d({ax, ay, az}).length(); if (om == 0.) return PIQuaternion(PIMathVectorT3d(), 1.); PIQuaternion res; res.q[0] = cos(om / 2); res.q[1] = ax / om * sin(om / 2); res.q[2] = ay / om * sin(om / 2); res.q[3] = az / om * sin(om / 2); return res; } PIQuaternion PIQuaternion::fromRotationMatrix(const PIMathMatrixT33d & m) { PIQuaternion ret; float tr = m[0][0] + m[1][1] + m[2][2]; if (tr > 0.0f) { ret.q[0] = tr + 1.0f; ret.q[1] = m[2][1] - m[1][2]; ret.q[2] = m[0][2] - m[2][0]; ret.q[3] = m[1][0] - m[0][1]; } else { if ((m[0][0] > m[1][1]) && (m[0][0] > m[2][2])) { ret.q[0] = m[2][1] - m[1][2]; ret.q[1] = 1.0f + m[0][0] - m[1][1] - m[2][2]; ret.q[2] = m[0][1] + m[1][0]; ret.q[3] = m[0][2] + m[2][0]; } else { if (m[1][1] > m[2][2]) { ret.q[0] = m[0][2] - m[2][0]; ret.q[1] = m[0][1] + m[1][0]; ret.q[2] = 1.0f + m[1][1] - m[0][0] - m[2][2]; ret.q[3] = m[1][2] + m[2][1]; } else { ret.q[0] = m[1][0] - m[0][1]; ret.q[1] = m[0][2] + m[2][0]; ret.q[2] = m[1][2] + m[2][1]; ret.q[3] = 1.0f + m[2][2] - m[0][0] - m[1][1]; } } } ret.normalize(); return ret; } PIQuaternion operator*(const PIQuaternion & q0, const PIQuaternion & q1) { PIMathVectorT3d v0(q0.vector()), v1(q1.vector()); double r0 = q0.q[0] * q1.q[0] - v0.dot(v1); PIMathVectorT3d qv = v1 * q0.q[0] + v0 * q1.q[0] + v0.cross(v1); PIQuaternion ret; ret.q[0] = r0; ret.q[1] = qv[0]; ret.q[2] = qv[1]; ret.q[3] = qv[2]; return ret; } PIQuaternion operator*(const double & a, const PIQuaternion & q1) { PIQuaternion ret(q1); ret.q[0] *= a; ret.q[1] *= a; ret.q[2] *= a; ret.q[3] *= a; return ret; }