/* SPDX-FileCopyrightText: Copyright (c) 2025 - 2026, the adamantine authors. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include #include #include #include #include namespace adamantine { Quaternion::Quaternion() { _quaternion = {{std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN()}}; _rotation_matrix = dealii::numbers::signaling_nan>(); _inv_rotation_matrix = dealii::numbers::signaling_nan>(); _vec_rotation_matrix = dealii::numbers::signaling_nan< dealii::Tensor<2, 3, dealii::VectorizedArray>>(); _vec_inv_rotation_matrix = dealii::numbers::signaling_nan< dealii::Tensor<2, 3, dealii::VectorizedArray>>(); } Quaternion::Quaternion(double const r, double const i, double const j, double const k) { reinit(r, i, j, k); } void Quaternion::reinit(double const r, double const i, double const j, double const k) { ASSERT(std::abs(r * r + i * i + j * j + k * k - 1.0) < 1e-10, "The quaternion is not normed."); _is_valid = true; // Store the quaternion _quaternion = {{r, i, j, k}}; build_rotation_matrices(); } bool Quaternion::is_valid() const { return _is_valid; } bool Quaternion::operator==(Quaternion const &other) const { ASSERT(_is_valid, "Quaternion is not initialized"); ASSERT(other.is_valid(), "Quaternion is not initialized"); return _quaternion == other._quaternion; } Quaternion &Quaternion::operator*=(Quaternion const &other) { ASSERT(_is_valid, "Quaternion is not initialized"); ASSERT(other.is_valid(), "Quaternion is not initialized"); mult(other._quaternion[0], other._quaternion[1], other._quaternion[2], other._quaternion[3]); return *this; } Quaternion &Quaternion::operator*=(double const &scalar) { for (int i = 0; i < 4; ++i) { _quaternion[i] *= scalar; } return *this; } Quaternion &Quaternion::operator/=(Quaternion const &other) { ASSERT(_is_valid, "Quaternion is not initialized"); ASSERT(other.is_valid(), "Quaternion is not initialized"); // Multiply by the inverse of the other quaternion mult(other._quaternion[0], -other._quaternion[1], -other._quaternion[2], -other._quaternion[3]); return *this; } void Quaternion::pow(double const exp) { ASSERT(_is_valid, "Quaternion is not initialized"); double const partial_norm = std::sqrt(_quaternion[1] * _quaternion[1] + _quaternion[2] * _quaternion[2] + _quaternion[3] * _quaternion[3]); std::array const unit_vector = {{_quaternion[1] / partial_norm, _quaternion[2] / partial_norm, _quaternion[3] / partial_norm}}; double theta = std::atan2(partial_norm, _quaternion[0]); _quaternion[0] = std::cos(exp * theta); _quaternion[1] = unit_vector[0] * std::sin(exp * theta); _quaternion[2] = unit_vector[1] * std::sin(exp * theta); _quaternion[3] = unit_vector[2] * std::sin(exp * theta); } void Quaternion::build_rotation_matrices() { double r = _quaternion[0]; double i = _quaternion[1]; double j = _quaternion[2]; double k = _quaternion[3]; // Build the rotation matrix _rotation_matrix[0][0] = 1.0 - 2.0 * (j * j + k * k); _rotation_matrix[0][1] = 2.0 * (i * j - k * r); _rotation_matrix[0][2] = 2.0 * (i * k + j * r); _rotation_matrix[1][0] = 2.0 * (i * j + k * r); _rotation_matrix[1][1] = 1.0 - 2.0 * (i * i + k * k); _rotation_matrix[1][2] = 2.0 * (j * k - i * r); _rotation_matrix[2][0] = 2.0 * (i * k - j * r); _rotation_matrix[2][1] = 2.0 * (j * k + i * r); _rotation_matrix[2][2] = 1.0 - 2.0 * (i * i + j * j); // Build the vectorized rotation matrix for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { _vec_rotation_matrix[i][j] = _rotation_matrix[i][j]; } } // Build the inverse rotation matrix. The inverse of a unit quaternion is // obtained by changing the sign of its imaginay components, i.e., the inverse // of (r,i,j,k) is (r,-i,-j,-k). _inv_rotation_matrix[0][0] = 1.0 - 2.0 * (j * j + k * k); _inv_rotation_matrix[0][1] = 2.0 * (i * j + k * r); _inv_rotation_matrix[0][2] = 2.0 * (i * k - j * r); _inv_rotation_matrix[1][0] = 2.0 * (i * j - k * r); _inv_rotation_matrix[1][1] = 1.0 - 2.0 * (i * i + k * k); _inv_rotation_matrix[1][2] = 2.0 * (j * k + i * r); _inv_rotation_matrix[2][0] = 2.0 * (i * k + j * r); _inv_rotation_matrix[2][1] = 2.0 * (j * k - i * r); _inv_rotation_matrix[2][2] = 1.0 - 2.0 * (i * i + j * j); // Build the vectorized inverse rotation matrix for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { _vec_inv_rotation_matrix[i][j] = _inv_rotation_matrix[i][j]; } } } void Quaternion::mult(double const other_r, double const other_i, double const other_j, double const other_k) { double r = _quaternion[0]; double i = _quaternion[1]; double j = _quaternion[2]; double k = _quaternion[3]; _quaternion[0] = r * other_r - i * other_i - j * other_j - k * other_k; _quaternion[1] = r * other_i + i * other_r + j * other_k - k * other_j; _quaternion[2] = r * other_j - i * other_k + j * other_r + k * other_i; _quaternion[3] = r * other_k + i * other_j - j * other_i + k * other_r; } double dot_product(Quaternion const &p, Quaternion const &q) { return std::inner_product(p._quaternion.begin(), p._quaternion.end(), q._quaternion.begin(), 0.); } } // namespace adamantine