Some optimizable aspects of Quat.hpp and Quat.cpp are:
-
Make read-only operations const.
Functions such as multiplication, norm calculation, conjugation, inversion, normalization, scaling, and rotation should not modify the source quaternion.
Quat operator*(const Quat& rhs) const;
double normSquared() const;
Quat inverse() const;
-
Mark non-throwing operations noexcept.
Pure arithmetic operations such as multiplication, scaling, conjugation, and squared-norm calculation can generally be noexcept. Do not mark inverse() or normalization noexcept if they throw for a zero quaternion.
-
Use [[nodiscard]] for returned mathematical results.
This helps detect accidentally discarded results, such as:
rotQuat.normalized(); // warning if result is ignored
q1 * q2; // warning if product is ignored
-
Separate copy-returning and in-place operations.
Provide both forms where useful:
[[nodiscard]] Quat scaled(double scalar) const noexcept;
Quat& operator*=(double scalar) noexcept;
[[nodiscard]] Quat normalized() const;
Quat& normalize();
This also prevents bugs such as calling unit() and discarding its returned quaternion.
-
Prefer conventional compound operators.
Use operator*= rather than names such as scaled_overwrite():
Quat& operator*=(double scalar) noexcept;
Quat& operator*=(const Quat& rhs) noexcept;
-
Add output-parameter overloads only for proven hot paths.
An API such as:
void multiply(const Quat& rhs, Quat& output) const noexcept;
may be useful when writing directly into preallocated storage, but it must correctly support or explicitly reject aliasing.
-
Avoid recomputing the inverse for batch rotations.
The largest quaternion-specific optimization is to calculate the inverse once when one quaternion rotates many vectors:
const Quat inverse = rotation.inverse();
for (Vector& vec : vectors) {
rotation.rotate(vec, inverse);
}
-
Introduce a prepared rotation abstraction.
Store a quaternion together with its precomputed inverse:
const auto prepared = rotation.prepareRotation();
for (Vector& vec : vectors) {
prepared.rotate(vec);
}
This is safer than allowing callers to accidentally pass an inverse belonging to another quaternion.
-
Retain the existing one-off rotation interface.
Keep a convenient overload that calculates the inverse internally:
void rotate(Vector& vec) const;
Add a faster overload for repeated applications:
void rotate(Vector& vec, const Quat& inverse) const noexcept;
-
Handle zero quaternions explicitly.
inverse() and normalization currently divide by zero when the norm is zero. Either throw an exception, assert, return an error indicator, or document NaN/infinity behavior.
-
Use std::sqrt and floating-point literals explicitly.
return std::sqrt(normSquared());
return conjugate().scaled(1.0 / squaredNorm);
Some optimizable aspects of
Quat.hppandQuat.cppare:Make read-only operations
const.Functions such as multiplication, norm calculation, conjugation, inversion, normalization, scaling, and rotation should not modify the source quaternion.
Mark non-throwing operations
noexcept.Pure arithmetic operations such as multiplication, scaling, conjugation, and squared-norm calculation can generally be
noexcept. Do not markinverse()or normalizationnoexceptif they throw for a zero quaternion.Use
[[nodiscard]]for returned mathematical results.This helps detect accidentally discarded results, such as:
Separate copy-returning and in-place operations.
Provide both forms where useful:
This also prevents bugs such as calling
unit()and discarding its returned quaternion.Prefer conventional compound operators.
Use
operator*=rather than names such asscaled_overwrite():Add output-parameter overloads only for proven hot paths.
An API such as:
may be useful when writing directly into preallocated storage, but it must correctly support or explicitly reject aliasing.
Avoid recomputing the inverse for batch rotations.
The largest quaternion-specific optimization is to calculate the inverse once when one quaternion rotates many vectors:
Introduce a prepared rotation abstraction.
Store a quaternion together with its precomputed inverse:
This is safer than allowing callers to accidentally pass an inverse belonging to another quaternion.
Retain the existing one-off rotation interface.
Keep a convenient overload that calculates the inverse internally:
Add a faster overload for repeated applications:
Handle zero quaternions explicitly.
inverse()and normalization currently divide by zero when the norm is zero. Either throw an exception, assert, return an error indicator, or document NaN/infinity behavior.Use
std::sqrtand floating-point literals explicitly.