Skip to content
Snippets Groups Projects
Quat.cpp 21.7 KiB
Newer Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidKernel/Logger.h"
#include "MantidKernel/Matrix.h"
#include "MantidKernel/V3D.h"

#include <boost/algorithm/string.hpp>
#include <sstream>
namespace Mantid {
namespace Kernel {
namespace {
Logger g_log("Quat");
}
 * Initialize the quaternion with the identity q=1.0+0i+0j+0k;
 */
Quat::Quat() : w(1), a(0), b(0), c(0) {}
 * Construct a Quat between two vectors;
 * The angle between them is defined differently from usual if vectors are not
 *unit or the same length vectors, so quat would be not consistent
 * w=v.des
 * (a,b,c)=(v x des)
 * @param src :: the source position
 * @param des :: the destination position
Quat::Quat(const V3D &src, const V3D &des) {
  const V3D v = Kernel::normalize(src + des);
  const V3D cross = v.cross_prod(des);

  if (cross.nullVector()) {
    double norm = a * a + b * b + c * c + w * w;
    if (fabs(norm - 1) > FLT_EPSILON) {
      norm = sqrt(norm);
      w /= norm;
      a /= norm;
      b /= norm;
      c /= norm;
    }
Quat::Quat(const Kernel::DblMatrix &RotMat) { this->setQuat(RotMat); }

//! Constructor with values
Quat::Quat(const double _w, const double _a, const double _b, const double _c)
    : w(_w), a(_a), b(_b), c(_c) {}
 * This construct a  quaternion to represent a rotation
 * of an angle _deg around the _axis. The _axis does not need to be a unit
 *vector
 * @param _deg :: angle of rotation
 * @param _axis :: axis to rotate about
Quat::Quat(const double _deg, const V3D &_axis) { setAngleAxis(_deg, _axis); }
 * Construct a Quaternion that performs a reference frame rotation.
 *  Specify the X,Y,Z vectors of the rotated reference frame, assuming that
 *  the initial X,Y,Z vectors are aligned as expected: X=(1,0,0), Y=(0,1,0),
 *Z=(0,0,1).
 *  The resuting quaternion rotates XYZ axes onto the provided rX, rY, rZ.
 *
 * @param rX :: rotated X reference axis; unit vector.
 * @param rY :: rotated Y reference axis; unit vector.
 * @param rZ :: rotated Z reference axis; unit vector.
Quat::Quat(const V3D &rX, const V3D &rY, const V3D &rZ) {
  // Call the operator to do the setting
  this->operator()(rX, rY, rZ);
}

/** Sets the quat values from four doubles
 * @param ww :: the value for w
 * @param aa :: the value for a
 * @param bb :: the value for b
 * @param cc :: the value for c
void Quat::set(const double ww, const double aa, const double bb,
               const double cc) {
  w = ww;
  a = aa;
  b = bb;
  c = cc;
/** Constructor from an angle and axis.
 * @param _deg :: angle of rotation
 * @param _axis :: axis to rotate about
 *
 * This construct a  quaternion to represent a rotation
 * of an angle _deg around the _axis. The _axis does not need to be a unit
 *vector
void Quat::setAngleAxis(const double _deg, const V3D &_axis) {
  double deg2rad = M_PI / 180.0;
  w = cos(0.5 * _deg * deg2rad);
  double s = sin(0.5 * _deg * deg2rad);
  const V3D temp = Kernel::normalize(_axis);
  a = s * temp[0];
  b = s * temp[1];
  c = s * temp[2];
}

bool Quat::isNull(const double tolerance) const {
  double pw = fabs(w) - 1;
  return (fabs(pw) < tolerance);
/// Extracts the angle of roatation and axis
/// @param _deg :: the angle of rotation
/// @param _ax0 :: The first component of the axis
/// @param _ax1 :: The second component of the axis
/// @param _ax2 :: The third component of the axis
void Quat::getAngleAxis(double &_deg, double &_ax0, double &_ax1,
                        double &_ax2) const {
  // If it represents a rotation of 0(2\pi), get an angle of 0 and axis (0,0,1)
  if (isNull(1e-5)) {
    _deg = 0;
    _ax0 = 0;
    _ax1 = 0;
    _ax2 = 1.0;
    return;
  }
  // Semi-angle in radians
  _deg = acos(w);
  // Prefactor for the axis part
  double s = sin(_deg);
  // Angle in degrees
  _deg *= 360.0 / M_PI;
  _ax0 = a / s;
  _ax1 = b / s;
  _ax2 = c / s;
/** Set the rotation (but don't change rotation axis).
void Quat::setRotation(const double deg) {
  double _deg, ax0, ax1, ax2;
  this->getAngleAxis(_deg, ax0, ax1, ax2);
  setAngleAxis(deg, V3D(ax0, ax1, ax2));
}

/** Sets the quat values from four doubles
 * @param ww :: the value for w
 * @param aa :: the value for a
 * @param bb :: the value for b
 * @param cc :: the value for c
void Quat::operator()(const double ww, const double aa, const double bb,
                      const double cc) {
  this->set(ww, aa, bb, cc);
}

/** Sets the quat values from an angle and a vector
 * @param angle :: the numbers of degrees
 * @param axis :: the axis of rotation
void Quat::operator()(const double angle, const V3D &axis) {
  this->setAngleAxis(angle, axis);
Nick Draper's avatar
Nick Draper committed
/** Sets the quat values from another Quat
Nick Draper's avatar
Nick Draper committed
 */
void Quat::operator()(const Quat &q) {
  w = q.w;
  a = q.a;
  b = q.b;
  c = q.c;
 * Set a Quaternion that performs a reference frame rotation.
 *  Specify the X,Y,Z vectors of the rotated reference frame, assuming that
 *  the initial X,Y,Z vectors are aligned as expected: X=(1,0,0), Y=(0,1,0),
 *Z=(0,0,1).
 *  The resuting quaternion rotates XYZ axes onto the provided rX, rY, rZ.
 *
 * @param rX :: rotated X reference axis; unit vector.
 * @param rY :: rotated Y reference axis; unit vector.
 * @param rZ :: rotated Z reference axis; unit vector.
void Quat::operator()(const V3D &rX, const V3D &rY, const V3D &rZ) {
  // The quaternion will combine two quaternions.
  (void)rZ; // Avoid compiler warning
  // These are the original axes
  constexpr V3D oX = V3D(1., 0., 0.);
  constexpr V3D oY = V3D(0., 1., 0.);
  // Axis that rotates X
  const V3D ax1 = oX.cross_prod(rX);
  // Create the first quaternion
  Quat Q1;
  if (!ax1.nullVector()) {
    // Rotation angle from oX to rX
    const double angle1 = oX.angle(rX);
    Q1.setAngleAxis(angle1 * 180.0 / M_PI, ax1);
  }
  // Now we rotate the original Y using Q1
  // Find the axis that rotates oYr onto rY
  const V3D ax2 = roY.cross_prod(rY);
  Quat Q2;
  if (!ax2.nullVector()) {
    const double angle2 = roY.angle(rY);
    Q2.setAngleAxis(angle2 * 180.0 / M_PI, ax2);
  }
  // Final = those two rotations in succession; Q1 is done first.
  const Quat final = Q2 * Q1;
void Quat::init() {
  w = 1.0;
  a = b = c = 0.0;
/** Quaternion addition operator
 * @param _q :: the quaternion to add
 * @return *this+_q
Quat Quat::operator+(const Quat &_q) const {
  return Quat(w + _q.w, a + _q.a, b + _q.b, c + _q.c);
/** Quaternion self-addition operator
 * @param _q :: the quaternion to add
 * @return *this+=_q
Quat &Quat::operator+=(const Quat &_q) {
  w += _q.w;
  a += _q.a;
  b += _q.b;
  c += _q.c;
  return *this;
/** Quaternion subtraction operator
 * @param _q :: the quaternion to add
 * @return *this-_q
Quat Quat::operator-(const Quat &_q) const {
  return Quat(w - _q.w, a - _q.a, b - _q.b, c - _q.c);
/** Quaternion self-substraction operator
 * @param _q :: the quaternion to add
 * @return *this-=_q
Quat &Quat::operator-=(const Quat &_q) {
  w -= _q.w;
  a -= _q.a;
  b -= _q.b;
  c -= _q.c;
  return *this;
/** Quaternion multiplication operator
 * @param _q :: the quaternion to multiply
 * @return *this*_q
 *
 *  Quaternion multiplication is non commutative
 *  in the same way multiplication of rotation matrices
 *  isn't.
 */
Quat Quat::operator*(const Quat &_q) const {
  double w1, a1, b1, c1;
  w1 = w * _q.w - a * _q.a - b * _q.b - c * _q.c;
  a1 = w * _q.a + _q.w * a + b * _q.c - _q.b * c;
  b1 = w * _q.b + _q.w * b - a * _q.c + c * _q.a;
  c1 = w * _q.c + _q.w * c + a * _q.b - _q.a * b;
  return Quat(w1, a1, b1, c1);
/** Quaternion self-multiplication operator
 * @param _q :: the quaternion to multiply
 * @return *this*=_q
Quat &Quat::operator*=(const Quat &_q) {
  double w1, a1, b1, c1;
  w1 = w * _q.w - a * _q.a - b * _q.b - c * _q.c;
  a1 = w * _q.a + _q.w * a + b * _q.c - _q.b * c;
  b1 = w * _q.b + _q.w * b - a * _q.c + c * _q.a;
  c1 = w * _q.c + _q.w * c + a * _q.b - _q.a * b;
  w = w1;
  a = a1;
  b = b1;
  c = c1;
  return (*this);
/** Quaternion equal operator
 * @param q :: the quaternion to compare
 *
 * Compare two quaternions at 1e-6%tolerance.
 * Use boost close_at_tolerance method
Nick Draper's avatar
Nick Draper committed
 * @return true if equal
bool Quat::operator==(const Quat &q) const {
  return !(fabs(w - q.w) > Tolerance || fabs(a - q.a) > Tolerance ||
           fabs(b - q.b) > Tolerance || fabs(c - q.c) > Tolerance);
  // return (quat_tol(w,q.w) && quat_tol(a,q.a) && quat_tol(b,q.b) &&
  // quat_tol(c,q.c));
/** Quaternion non-equal operator
 * @param _q :: the quaternion to compare
 *
 * Compare two quaternions at 1e-6%tolerance.
 *  Use boost close_at_tolerance method
bool Quat::operator!=(const Quat &_q) const { return (!operator==(_q)); }
 *
 * Divide all elements by the quaternion norm
 */
void Quat::normalize() {
  double overnorm;
  if (len2() == 0)
    overnorm = 1.0;
  else
    overnorm = 1.0 / len();
  w *= overnorm;
  a *= overnorm;
  b *= overnorm;
  c *= overnorm;
 *
 *  Reverse the sign of the 3 imaginary components of the
 *  quaternion
 */
void Quat::conjugate() {
  a *= -1.0;
  b *= -1.0;
  c *= -1.0;
double Quat::len() const { return sqrt(len2()); }
/** Quaternion norm (length squared)
 * @return the length squared
double Quat::len2() const { return (w * w + a * a + b * b + c * c); }
void Quat::inverse() {
  conjugate();
  double overnorm = len2();
  if (overnorm == 0)
    overnorm = 1.0;
  else
    overnorm = 1.0 / overnorm;
  w *= overnorm;
  a *= overnorm;
  b *= overnorm;
  c *= overnorm;
/** 	Rotate a vector.
 *  @param v :: the vector to be rotated
 *
 *   The quaternion needs to be normalized beforehand to
 *   represent a rotation. If q is thequaternion, the rotation
 *   is represented by q.v.q-1 where q-1 is the inverse of
 *   v.
 */
void Quat::rotate(V3D &v) const {
  Quat qinvert(*this);
  qinvert.inverse();
  Quat pos(0.0, v[0], v[1], v[2]);
  pos *= qinvert;
  pos = (*this) * pos;
  v[0] = pos[1];
  v[1] = pos[2];
  v[2] = pos[3];
}
/** Convert quaternion rotation to an OpenGL matrix [4x4] matrix
Nick Draper's avatar
Nick Draper committed
 * stored as an linear array of 16 double
 * The function glRotated must be called
void Quat::GLMatrix(double *mat) const {
  double aa = a * a;
  double ab = a * b;
  double ac = a * c;
  double aw = a * w;
  double bb = b * b;
  double bc = b * c;
  double bw = b * w;
  double cc = c * c;
  double cw = c * w;
  *mat = 1.0 - 2.0 * (bb + cc);
  ++mat;
  *mat = 2.0 * (ab + cw);
  ++mat;
  *mat = 2.0 * (ac - bw);
  ++mat;
  *mat = 0;
  ++mat;
  *mat = 2.0 * (ab - cw);
  ++mat;
  *mat = 1.0 - 2.0 * (aa + cc);
  ++mat;
  *mat = 2.0 * (bc + aw);
  ++mat;
  *mat = 0;
  ++mat;
  *mat = 2.0 * (ac + bw);
  mat++;
  *mat = 2.0 * (bc - aw);
  mat++;
  *mat = 1.0 - 2.0 * (aa + bb);
  mat++;
  for (int i = 0; i < 4; ++i) {
    *mat = 0;
    mat++;
  }
  *mat = 1.0;
}
/// using convention at
/// http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
std::vector<double> Quat::getRotation(bool check_normalisation,
                                      bool throw_on_errors) const {
  double aa = a * a;
  double ab = a * b;
  double ac = a * c;
  double aw = a * w;
  double bb = b * b;
  double bc = b * c;
  double bw = b * w;
  double cc = c * c;
  double cw = c * w;
  if (check_normalisation) {
    double normSq = aa + bb + cc + w * w;
    if (fabs(normSq - 1) > FLT_EPSILON) {
      if (throw_on_errors) {
        g_log.error() << " A non-unit quaternion used to obtain a rotation "
                         "matrix; need to notmalize it first\n";
        throw(std::invalid_argument("Attempt to use non-normalized quaternion "
                                    "to define rotation matrix; need to "
                                    "notmalize it first"));
      } else {
        g_log.information() << " Warning; a non-unit quaternion used to obtain "
                               "the rotation matrix; using normalized quat\n";
        aa /= normSq;
        ab /= normSq;
        ac /= normSq;
        aw /= normSq;
        bb /= normSq;
        bc /= normSq;
        bw /= normSq;
        cc /= normSq;
        cw /= normSq;
      }
    }
  }
  std::vector<double> out(9);

  out[0] = (1.0 - 2.0 * (bb + cc));
  out[1] = 2.0 * (ab - cw);
  out[2] = 2.0 * (ac + bw);

  out[3] = 2.0 * (ab + cw);
  out[4] = (1.0 - 2.0 * (aa + cc));
  out[5] = 2.0 * (bc - aw);

  out[6] = 2.0 * (ac - bw);
  out[7] = 2.0 * (bc + aw);
  out[8] = (1.0 - 2.0 * (aa + bb));
  return out;
 * Converts the GL Matrix into Quat
 */
void Quat::setQuat(double mat[16]) {
  double tr, s, q[4];
  int nxt[3] = {1, 2, 0};
  tr = mat[0] + mat[5] + mat[10];
  if (tr > 0.0) {
    s = sqrt(tr + 1.0);
    w = s / 2.0;
    s = 0.5 / s;
    a = (mat[6] - mat[9]) * s;
    b = (mat[8] - mat[2]) * s;
    c = (mat[1] - mat[4]) * s;
  } else {
    int i = 0;
    if (mat[5] > mat[0])
      i = 1;
    if (mat[10] > mat[i * 5])
      i = 2;
    int j = nxt[i];
    int k = nxt[j];
    s = sqrt(mat[i * 5] - (mat[j * 5] + mat[k * 5]) + 1.0);
    q[i] = s * 0.5;
    if (s != 0.0)
      s = 0.5 / s;
    q[3] = (mat[j * 4 + k] - mat[k * 4 + j]) * s;
    q[j] = (mat[i * 4 + j] + mat[j * 4 + i]) * s;
    q[k] = (mat[i * 4 + k] + mat[k * 4 + i]) * s;
    a = q[0];
    b = q[1];
    c = q[2];
    w = q[3];
  }
}
/// Using the convention at
/// http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
void Quat::setQuat(const Kernel::DblMatrix &rMat) {
  int i = 0, j, k;
  if (rMat[1][1] > rMat[0][0])
    i = 1;
  if (rMat[2][2] > rMat[1][1])
    i = 2;
  j = (i + 1) % 3;
  k = (j + 1) % 3;
  double r = sqrt(1. + rMat[i][i] - rMat[j][j] - rMat[k][k]);
  if (r == 0) {
    a = 0.;
    b = 0.;
    c = 0.;
    w = 1.;
  } else {
    double q[4], f = 0.5 / r;
    q[i] = 0.5 * r;
    q[j] = f * (rMat[i][j] + rMat[j][i]);
    q[k] = f * (rMat[k][i] + rMat[i][k]);
    q[3] = f * (rMat[k][j] - rMat[j][k]);
    a = q[0];
    b = q[1];
    c = q[2];
    w = q[3];
  }
}
/** Bracket operator overload
 * returns the internal representation values based on an index
 * @param Index :: the index of the value required 0=w, 1=a, 2=b, 3=c
 * @returns a double of the value requested
 */
double Quat::operator[](const int Index) const {
  switch (Index) {
  case 0:
    return w;
  case 1:
    return a;
  case 2:
    return b;
  case 3:
    return c;
  default:
    throw std::runtime_error("Quat::operator[] range error");
  }
}

/** Bracket operator overload
 * returns the internal representation values based on an index
 * @param Index :: the index of the value required 0=w, 1=a, 2=b, 3=c
 * @returns a double of the value requested
 */
double &Quat::operator[](const int Index) {
  switch (Index) {
  case 0:
    return w;
  case 1:
    return a;
  case 2:
    return b;
  case 3:
    return c;
  default:
    throw std::runtime_error("Quat::operator[] range error");
  }
}

/** Prints a string representation of itself
void Quat::printSelf(std::ostream &os) const {
  os << "[" << w << "," << a << "," << b << "," << c << "]";
/**  Read data from a stream in the format returned by printSelf ("[w,a,b,c]").
 *   @param IX :: Input Stream
 *   @throw std::runtime_error if the input is of wrong format
void Quat::readPrinted(std::istream &IX) {
  std::string in;
  std::getline(IX, in);
  size_t i = in.find_first_of('[');
  if (i == std::string::npos)
    throw std::runtime_error("Wrong format for Quat input: " + in);
  size_t j = in.find_last_of(']');
  if (j == std::string::npos || j < i + 8)
    throw std::runtime_error("Wrong format for Quat input: " + in);

  size_t c1 = in.find_first_of(',');
  size_t c2 = in.find_first_of(',', c1 + 1);
  size_t c3 = in.find_first_of(',', c2 + 1);
  if (c1 == std::string::npos || c2 == std::string::npos ||
      c3 == std::string::npos)
    throw std::runtime_error("Wrong format for Quat input: [" + in + "]");

  w = std::stod(in.substr(i + 1, c1 - i - 1));
  a = std::stod(in.substr(c1 + 1, c2 - c1 - 1));
  b = std::stod(in.substr(c2 + 1, c3 - c2 - 1));
  c = std::stod(in.substr(c3 + 1, j - c3 - 1));
/** Prints a string representation
 * @param os :: the stream to output to
 * @param q :: the quat to output
 * @returns the stream
 */
std::ostream &operator<<(std::ostream &os, const Quat &q) {
  q.printSelf(os);
  return os;
}

/**  Reads in a quat from an input stream
 *   @param ins :: The input stream
 *   @param q :: The quat
std::istream &operator>>(std::istream &ins, Quat &q) {
  q.readPrinted(ins);
  return ins;
/** @return the quat as a string "[w,a,b,c]" */
std::string Quat::toString() const {
  std::ostringstream mess;
  this->printSelf(mess);
  return mess.str();
}

/** Sets the Quat using a string
 * @param str :: the Quat as a string "[w,a,b,c]" */
void Quat::fromString(const std::string &str) {
  std::istringstream mess(str);
  this->readPrinted(mess);
}

void Quat::rotateBB(double &xmin, double &ymin, double &zmin, double &xmax,
                    double &ymax, double &zmax) const {
  // Defensive
  if (xmin > xmax)
    std::swap(xmin, xmax);
  if (ymin > ymax)
    std::swap(ymin, ymax);
  if (zmin > zmax)
    std::swap(zmin, zmax);
  // Get the min and max of the cube, and remove centring offset
  Mantid::Kernel::V3D minT(xmin, ymin, zmin), maxT(xmax, ymax, zmax);
  // Get the rotation matrix
  double rotMatr[16];
  GLMatrix(&rotMatr[0]);
  // Now calculate new min and max depending on the sign of matrix components
  // Much faster than creating 8 points and rotate them. The new min (max)
  // can only be obtained by summing the smallest (largest) components
  //
  Mantid::Kernel::V3D minV, maxV;
  // Looping on rows of matrix
  int index;
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      index = j + i * 4; // The OpenGL matrix is linear and represent a 4x4
                         // matrix but only the 3x3 upper-left inner part
      // contains the rotation
      minV[j] += (rotMatr[index] > 0) ? rotMatr[index] * minT[i]
                                      : rotMatr[index] * maxT[i];
      maxV[j] += (rotMatr[index] > 0) ? rotMatr[index] * maxT[i]
                                      : rotMatr[index] * minT[i];
    }
  }
  // Adjust value.
  xmin = minV[0];
  ymin = minV[1];
  zmin = minV[2];
  xmax = maxV[0];
  ymax = maxV[1];
  zmax = maxV[2];
/** Calculate the Euler angles that are equivalent to this Quaternion.
 * Euler angles are calculated intrinsically, i.e. the first rotation modifies
 *the axis used for
 * the second rotation, and the second rotation modifies the axis used for the
 *third rotation.
 * You can specify which axis the rotations should be applied around and the
 *order in which they
 * are to be applied with the convention parameter. For instance, for a rotation
 *of Y and then
 * the new Z axis, and then the new Y axis: pass "YZY" as the convention. Or for
 *a rotation
 * such as X, and then the new Y axis, and then the new Z axis: pass "XYZ" as
 *the convention.
 * @param convention :: The axes to apply the rotations to and the order in
 *which to do so. Defaults to "XYZ".
 * @returns A vector of the Euler angles in degrees. The order of the angles is
 *the same as in the convention parameter.
std::vector<double>
Quat::getEulerAngles(const std::string &convention = "XYZ") const {
  std::string conv(convention);

  if (conv.length() != 3)
    throw std::invalid_argument("Wrong convention name (string length not 3)");

  boost::to_upper(conv);

  // Check it's only XYZ in the string
  if (conv.find_first_not_of("XYZ") != std::string::npos)
    throw std::invalid_argument(
        "Wrong convention name (characters other than XYZ)");
  // Cannot be XXY, XYY, or similar. Only first and last may be the same: YXY
  if ((conv[0] == conv[1]) || (conv[2] == conv[1]))
    throw std::invalid_argument("Wrong convention name (repeated indices)");

  boost::replace_all(conv, "X", "0");
  boost::replace_all(conv, "Y", "1");
  boost::replace_all(conv, "Z", "2");

  std::stringstream s;
  s << conv[0] << " " << conv[1] << " " << conv[2];

  int first, second, last;
  s >> first >> second >> last;

  // Do we want Tait-Bryan angles, as opposed to 'classic' Euler angles?
  const int TB =
      (first * second * last == 0 && first + second + last == 3) ? 1 : 0;
  const int par01 = ((second - first + 9) % 3 == 1) ? 1 : -1;
  const int par12 = ((last - second + 9) % 3 == 1) ? 1 : -1;

  std::vector<double> angles(3);

  const DblMatrix R = DblMatrix(this->getRotation());

  const int i = (last + TB * par12 + 9) % 3;
  const int j1 = (last - par12 + 9) % 3;
  const int j2 = (last + par12 + 9) % 3;

  const double s3 = (1.0 - TB - TB * par12) * R[i][j1];
  const double c3 = (TB - (1.0 - TB) * par12) * R[i][j2];

  V3D axis3(0, 0, 0);
  axis3[last] = 1.0;

  constexpr double rad2deg = 180.0 / M_PI;
  angles[2] = atan2(s3, c3) * rad2deg;

  DblMatrix Rm3(Quat(-angles[2], axis3).getRotation());
  DblMatrix Rp = R * Rm3;

  const double s1 =
      par01 * Rp[(first - par01 + 9) % 3][(first + par01 + 9) % 3];
  const double c1 = Rp[second][second];
  const double s2 = par01 * Rp[first][3 - first - second];
  const double c2 = Rp[first][first];

  angles[0] = atan2(s1, c1) * rad2deg;
  angles[1] = atan2(s2, c2) * rad2deg;
} // namespace Mantid