#pragma once #include namespace mlx { template constexpr EulerAngles::EulerAngles(DegreeAngle P, DegreeAngle Y, DegreeAngle R) : pitch(P), yaw(Y), roll(R) {} template constexpr EulerAngles::EulerAngles(const DegreeAngle angles[3]) : EulerAngles(angles[0], angles[1], angles[2]) {} template template constexpr EulerAngles::EulerAngles(const Angle& angle) : EulerAngles(angle.ToEulerAngles()) {} template constexpr EulerAngles::EulerAngles(const Quat& quat) : EulerAngles(quat.ToEulerAngles()) {} template template constexpr EulerAngles::EulerAngles(const EulerAngles& angles) : pitch(DegreeAngle(angles.pitch)), yaw(DegreeAngle(angles.yaw)), roll(DegreeAngle(angles.roll)) {} template constexpr bool EulerAngles::ApproxEqual(const EulerAngles& angles, T maxDifference) const { return pitch.ApproxEqual(angles.pitch, maxDifference) && yaw.ApproxEqual(angles.yaw, maxDifference) && roll.ApproxEqual(angles.roll, maxDifference); } template constexpr EulerAngles& EulerAngles::Normalize() { pitch.Normalize(); yaw.Normalize(); roll.Normalize(); return *this; } template Quat EulerAngles::ToQuat() const { // XYZ auto [s1, c1] = (yaw / T(2.0)).GetSinCos(); auto [s2, c2] = (roll / T(2.0)).GetSinCos(); auto [s3, c3] = (pitch / T(2.0)).GetSinCos(); return Quat(c1 * c2 * c3 - s1 * s2 * s3, s1 * s2 * c3 + c1 * c2 * s3, s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3); } template std::string EulerAngles::ToString() const { std::ostringstream ss; ss << *this; return ss.str(); } template constexpr EulerAngles EulerAngles::operator+(const EulerAngles& angles) const { return EulerAngles(pitch + angles.pitch, yaw + angles.yaw, roll + angles.roll); } template constexpr EulerAngles EulerAngles::operator-(const EulerAngles& angles) const { return EulerAngles(pitch - angles.pitch, yaw - angles.yaw, roll - angles.roll); } template constexpr EulerAngles& EulerAngles::operator+=(const EulerAngles& angles) { pitch += angles.pitch; yaw += angles.yaw; roll += angles.roll; return *this; } template constexpr EulerAngles& EulerAngles::operator-=(const EulerAngles& angles) { pitch -= angles.pitch; yaw -= angles.yaw; roll -= angles.roll; return *this; } template constexpr bool EulerAngles::operator==(const EulerAngles& angles) const { return pitch == angles.pitch && yaw == angles.yaw && roll == angles.roll; } template constexpr bool EulerAngles::operator!=(const EulerAngles& angles) const { return !operator==(angles); } template constexpr bool EulerAngles::operator<(const EulerAngles& angles) const { if (pitch != angles.pitch) return pitch < angles.pitch; if (yaw != angles.yaw) return yaw < angles.yaw; return roll < angles.roll; } template constexpr bool EulerAngles::operator<=(const EulerAngles& angles) const { if (pitch != angles.pitch) return pitch < angles.pitch; if (yaw != angles.yaw) return yaw < angles.yaw; return roll <= angles.roll; } template constexpr bool EulerAngles::operator>(const EulerAngles& angles) const { if (pitch != angles.pitch) return pitch > angles.pitch; if (yaw != angles.yaw) return yaw > angles.yaw; return roll > angles.roll; } template constexpr bool EulerAngles::operator>=(const EulerAngles& angles) const { if (pitch != angles.pitch) return pitch > angles.pitch; if (yaw != angles.yaw) return yaw > angles.yaw; return roll >= angles.roll; } template constexpr bool EulerAngles::ApproxEqual(const EulerAngles& lhs, const EulerAngles& rhs, T maxDifference) { return lhs.ApproxEqual(rhs, maxDifference); } template constexpr EulerAngles EulerAngles::Zero() { return EulerAngles(0, 0, 0); } template std::ostream& operator<<(std::ostream& out, const EulerAngles& angles) { return out << "EulerAngles(" << angles.pitch << ", " << angles.yaw << ", " << angles.roll << ')'; } }