Converted all unit tests to template form
All checks were successful
Build and Run C++ Unit Tests / build-and-test (push) Successful in 2m2s

This commit is contained in:
Pascal Serrarens 2025-12-22 15:01:11 +01:00
parent 679471f748
commit b555fb5d6a
21 changed files with 1285 additions and 1011 deletions

View File

@ -43,24 +43,22 @@ const DirectionOf<T> DirectionOf<T>::right =
DirectionOf<T>(AngleOf<T>::Degrees(90), AngleOf<T>()); DirectionOf<T>(AngleOf<T>::Degrees(90), AngleOf<T>());
template <typename T> template <typename T>
Vector3 DirectionOf<T>::ToVector3() const { Vector3Of<float> DirectionOf<T>::ToVector3() const {
Quaternion q = Quaternion::Euler(-this->vertical.InDegrees(), Quaternion q = Quaternion::Euler(-this->vertical.InDegrees(),
this->horizontal.InDegrees(), 0); this->horizontal.InDegrees(), 0);
Vector3 v = q * Vector3::forward; Vector3Of<float> v = q * Vector3Of<float>::forward;
return v; return v;
} }
template <typename T> template <typename T>
DirectionOf<T> DirectionOf<T>::FromVector3(Vector3 vector) { DirectionOf<T> DirectionOf<T>::FromVector3(Vector3Of<float> vector) {
DirectionOf<T> d; DirectionOf<T> d;
d.horizontal = AngleOf<T>::Atan2( d.horizontal = AngleOf<T>::Atan2(
vector.Right(), vector.horizontal,
vector vector.depth); // AngleOf<T>::Radians(atan2f(v.Right(), v.Forward()));
.Forward()); // AngleOf<T>::Radians(atan2f(v.Right(), v.Forward())); d.vertical = AngleOf<T>::Degrees(-90) -
d.vertical = AngleOf<T>::Acos(vector.vertical); // AngleOf<T>::Radians(-(0.5f
AngleOf<T>::Degrees(-90) - // * pi) - acosf(v.Up()));
AngleOf<T>::Acos(
vector.Up()); // AngleOf<T>::Radians(-(0.5f * pi) - acosf(v.Up()));
d.Normalize(); d.Normalize();
return d; return d;
} }
@ -101,4 +99,4 @@ void DirectionOf<T>::Normalize() {
template class LinearAlgebra::DirectionOf<float>; template class LinearAlgebra::DirectionOf<float>;
template class LinearAlgebra::DirectionOf<signed short>; template class LinearAlgebra::DirectionOf<signed short>;
} } // namespace LinearAlgebra

View File

@ -9,7 +9,9 @@
namespace LinearAlgebra { namespace LinearAlgebra {
struct Vector3; //struct Vector3;
template <typename T>
class Vector3Of;
/// @brief A direction using angles in various representations /// @brief A direction using angles in various representations
/// @tparam T The implementation type used for the representation of the angles /// @tparam T The implementation type used for the representation of the angles
@ -38,13 +40,13 @@ class DirectionOf {
/// @brief Convert the direction into a carthesian vector /// @brief Convert the direction into a carthesian vector
/// @return The carthesian vector corresponding to this direction. /// @return The carthesian vector corresponding to this direction.
Vector3 ToVector3() const; Vector3Of<float> ToVector3() const;
/// @brief Convert a carthesian vector into a direction /// @brief Convert a carthesian vector into a direction
/// @param v The carthesian vector /// @param v The carthesian vector
/// @return The direction. /// @return The direction.
/// @note Information about the length of the carthesian vector is not /// @note Information about the length of the carthesian vector is not
/// included in this transformation. /// included in this transformation.
static DirectionOf<T> FromVector3(Vector3 vector); static DirectionOf<T> FromVector3(Vector3Of<float> vector);
/// @brief Create a direction using angle values in degrees /// @brief Create a direction using angle values in degrees
/// @param horizontal The horizontal angle in degrees /// @param horizontal The horizontal angle in degrees

View File

@ -36,11 +36,11 @@ PolarOf<T> PolarOf<T>::Radians(float distance, float radians) {
return PolarOf<T>(distance, AngleOf<T>::Radians(radians)); return PolarOf<T>(distance, AngleOf<T>::Radians(radians));
} }
template <typename T> template <>
PolarOf<T> PolarOf<T>::FromVector2(Vector2Of<T> v) { PolarOf<float> PolarOf<float>::FromVector2(Vector2Of<float> v) {
float distance = v.Magnitude(); float distance = v.Magnitude();
AngleOf<T> angle = Vector2Of<T>::SignedAngle(Vector2Of<T>::forward, v); AngleOf<float> angle = Vector2Of<float>::SignedAngle(Vector2Of<float>::forward, v);
PolarOf<T> p = PolarOf(distance, angle); PolarOf<float> p = PolarOf(distance, angle);
return p; return p;
} }
template <typename T> template <typename T>

View File

@ -56,7 +56,7 @@ class PolarOf {
/// @brief Convert a vector from 2D carthesian coordinates to polar /// @brief Convert a vector from 2D carthesian coordinates to polar
/// coordinates /// coordinates
/// @param v The vector to convert /// @param v The vector to convert
static PolarOf<T> FromVector2(Vector2Of<T> v); static PolarOf<float> FromVector2(Vector2Of<float> v);
/// @brief Convert a vector from spherical coordinates to polar coordinates /// @brief Convert a vector from spherical coordinates to polar coordinates
/// @param s The vector to convert /// @param s The vector to convert
/// @note The resulting vector will be projected on the horizontal plane /// @note The resulting vector will be projected on the horizontal plane

View File

@ -44,8 +44,8 @@ Quaternion::~Quaternion() {}
const Quaternion Quaternion::identity = Quaternion(0, 0, 0, 1); const Quaternion Quaternion::identity = Quaternion(0, 0, 0, 1);
Vector3 Quaternion::xyz() const { Vector3Of<float> Quaternion::xyz() const {
return Vector3(x, y, z); return Vector3Of<float>(x, y, z);
} }
float Quaternion::GetLength() const { float Quaternion::GetLength() const {
@ -78,18 +78,18 @@ float Quaternion::Dot(Quaternion a, Quaternion b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
} }
Vector3 Quaternion::ToAngles(const Quaternion& q1) { Vector3Of<float> Quaternion::ToAngles(const Quaternion& q1) {
float test = q1.x * q1.y + q1.z * q1.w; float test = q1.x * q1.y + q1.z * q1.w;
if (test > 0.499f) { // singularity at north pole if (test > 0.499f) { // singularity at north pole
return Vector3(0, 2 * (float)atan2(q1.x, q1.w) * Rad2Deg, 90); return Vector3Of<float>(0, 2 * (float)atan2(q1.x, q1.w) * Rad2Deg, 90);
} else if (test < -0.499f) { // singularity at south pole } else if (test < -0.499f) { // singularity at south pole
return Vector3(0, -2 * (float)atan2(q1.x, q1.w) * Rad2Deg, -90); return Vector3Of<float>(0, -2 * (float)atan2(q1.x, q1.w) * Rad2Deg, -90);
} else { } else {
float sqx = q1.x * q1.x; float sqx = q1.x * q1.x;
float sqy = q1.y * q1.y; float sqy = q1.y * q1.y;
float sqz = q1.z * q1.z; float sqz = q1.z * q1.z;
return Vector3( return Vector3Of<float>(
atan2f(2 * q1.x * q1.w - 2 * q1.y * q1.z, 1 - 2 * sqx - 2 * sqz) * atan2f(2 * q1.x * q1.w - 2 * q1.y * q1.z, 1 - 2 * sqx - 2 * sqz) *
Rad2Deg, Rad2Deg,
atan2f(2 * q1.y * q1.w - 2 * q1.x * q1.z, 1 - 2 * sqy - 2 * sqz) * atan2f(2 * q1.y * q1.w - 2 * q1.x * q1.z, 1 - 2 * sqy - 2 * sqz) *
@ -128,7 +128,7 @@ Quaternion Quaternion::operator*(const Quaternion& r2) const {
-this->x * r2.x - this->y * r2.y - this->z * r2.z + this->w * r2.w); -this->x * r2.x - this->y * r2.y - this->z * r2.z + this->w * r2.w);
}; };
Vector3 Quaternion::operator*(const Vector3& p) const { Vector3Of<float> Quaternion::operator*(const Vector3Of<float>& p) const {
float num = this->x * 2; float num = this->x * 2;
float num2 = this->y * 2; float num2 = this->y * 2;
float num3 = this->z * 2; float num3 = this->z * 2;
@ -142,9 +142,9 @@ Vector3 Quaternion::operator*(const Vector3& p) const {
float num11 = this->w * num2; float num11 = this->w * num2;
float num12 = this->w * num3; float num12 = this->w * num3;
float px = p.Right(); float px = p.horizontal;
float py = p.Up(); float py = p.vertical;
float pz = p.Forward(); float pz = p.depth;
// Vector3 result = Vector3::zero; // Vector3 result = Vector3::zero;
// result.x = // result.x =
float rx = float rx =
@ -155,7 +155,7 @@ Vector3 Quaternion::operator*(const Vector3& p) const {
// result.z = // result.z =
float rz = float rz =
(num8 - num11) * px + (num9 + num10) * py + (1 - (num4 + num5)) * pz; (num8 - num11) * px + (num9 + num10) * py + (1 - (num4 + num5)) * pz;
Vector3 result = Vector3(rx, ry, rz); Vector3Of<float> result = Vector3Of<float>(rx, ry, rz);
return result; return result;
} }
@ -168,23 +168,23 @@ Quaternion Quaternion::Inverse(Quaternion r) {
return Quaternion(-r.x / n, -r.y / n, -r.z / n, r.w / n); return Quaternion(-r.x / n, -r.y / n, -r.z / n, r.w / n);
} }
Quaternion Quaternion::LookRotation(const Vector3& forward) { Quaternion Quaternion::LookRotation(const Vector3Of<float>& forward) {
Vector3 up = Vector3(0, 1, 0); Vector3Of<float> up = Vector3Of<float>(0, 1, 0);
return LookRotation(forward, up); return LookRotation(forward, up);
} }
Quaternion Quaternion::LookRotation(const Vector3& forward, const Vector3& up) { Quaternion Quaternion::LookRotation(const Vector3Of<float>& forward, const Vector3Of<float>& up) {
Vector3 nForward = Vector3::Normalize(forward); Vector3Of<float> nForward = Vector3Of<float>::Normalize(forward);
Vector3 nRight = Vector3::Normalize(Vector3::Cross(up, nForward)); Vector3Of<float> nRight = Vector3Of<float>::Normalize(Vector3Of<float>::Cross(up, nForward));
Vector3 nUp = Vector3::Cross(nForward, nRight); Vector3Of<float> nUp = Vector3Of<float>::Cross(nForward, nRight);
float m00 = nRight.Right(); // x; float m00 = nRight.horizontal; // x;
float m01 = nRight.Up(); // y; float m01 = nRight.vertical; // y;
float m02 = nRight.Forward(); // z; float m02 = nRight.depth; // z;
float m10 = nUp.Right(); // x; float m10 = nUp.horizontal; // x;
float m11 = nUp.Up(); // y; float m11 = nUp.vertical; // y;
float m12 = nUp.Forward(); // z; float m12 = nUp.depth; // z;
float m20 = nForward.Right(); // x; float m20 = nForward.horizontal; // x;
float m21 = nForward.Up(); // y; float m21 = nForward.vertical; // y;
float m22 = nForward.Forward(); // z; float m22 = nForward.depth; // z;
float num8 = (m00 + m11) + m22; float num8 = (m00 + m11) + m22;
Quaternion quaternion = Quaternion(); Quaternion quaternion = Quaternion();
@ -224,11 +224,11 @@ Quaternion Quaternion::LookRotation(const Vector3& forward, const Vector3& up) {
return quaternion; return quaternion;
} }
Quaternion Quaternion::FromToRotation(Vector3 fromDirection, Quaternion Quaternion::FromToRotation(Vector3Of<float> fromDirection,
Vector3 toDirection) { Vector3Of<float> toDirection) {
Vector3 axis = Vector3::Cross(fromDirection, toDirection); Vector3Of<float> axis = Vector3Of<float>::Cross(fromDirection, toDirection);
axis = Vector3::Normalize(axis); axis = Vector3Of<float>::Normalize(axis);
AngleOf<float> angle = Vector3::SignedAngle(fromDirection, toDirection, axis); AngleOf<float> angle = Vector3Of<float>::SignedAngle(fromDirection, toDirection, axis);
Quaternion rotation = Quaternion::AngleAxis(angle.InDegrees(), axis); Quaternion rotation = Quaternion::AngleAxis(angle.InDegrees(), axis);
return rotation; return rotation;
} }
@ -244,18 +244,18 @@ Quaternion Quaternion::RotateTowards(const Quaternion& from,
return SlerpUnclamped(from, to, t); return SlerpUnclamped(from, to, t);
} }
Quaternion Quaternion::AngleAxis(float angle, const Vector3& axis) { Quaternion Quaternion::AngleAxis(float angle, const Vector3Of<float>& axis) {
if (Vector3::SqrMagnitude(axis) == 0.0f) if (Vector3Of<float>::SqrMagnitudeOf(axis) == 0.0f)
return Quaternion(); return Quaternion();
Quaternion result = Quaternion(); Quaternion result = Quaternion();
float radians = angle * Deg2Rad; float radians = angle * Deg2Rad;
radians *= 0.5f; radians *= 0.5f;
Vector3 axis2 = axis * (float)sin(radians); Vector3Of<float> axis2 = axis * (float)sin(radians);
result.x = axis2.Right(); // x; result.x = axis2.horizontal; // x;
result.y = axis2.Up(); // y; result.y = axis2.vertical; // y;
result.z = axis2.Forward(); // z; result.z = axis2.depth; // z;
result.w = (float)cos(radians); result.w = (float)cos(radians);
return Quaternion::Normalize(result); return Quaternion::Normalize(result);
@ -266,23 +266,23 @@ float Quaternion::Angle(Quaternion a, Quaternion b) {
return (float)acos(fmin(fabs(f), 1)) * 2 * Rad2Deg; return (float)acos(fmin(fabs(f), 1)) * 2 * Rad2Deg;
} }
void Quaternion::ToAngleAxis(float* angle, Vector3* axis) { void Quaternion::ToAngleAxis(float* angle, Vector3Of<float>* axis) {
Quaternion::ToAxisAngleRad(*this, axis, angle); Quaternion::ToAxisAngleRad(*this, axis, angle);
*angle *= Rad2Deg; *angle *= Rad2Deg;
} }
void Quaternion::ToAxisAngleRad(const Quaternion& q, void Quaternion::ToAxisAngleRad(const Quaternion& q,
Vector3* const axis, Vector3Of<float>* const axis,
float* angle) { float* angle) {
Quaternion q1 = (fabs(q.w) > 1.0f) ? Quaternion::Normalize(q) : q; Quaternion q1 = (fabs(q.w) > 1.0f) ? Quaternion::Normalize(q) : q;
*angle = 2.0f * acosf(q1.w); // angle *angle = 2.0f * acosf(q1.w); // angle
float den = sqrtf(1.0F - q1.w * q1.w); float den = sqrtf(1.0F - q1.w * q1.w);
if (den > 0.0001f) { if (den > 0.0001f) {
*axis = Vector3::Normalize(q1.xyz() / den); *axis = Vector3Of<float>::Normalize(q1.xyz() / den);
} else { } else {
// This occurs when the angle is zero. // This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis. // Not a problem: just set an arbitrary normalized axis.
*axis = Vector3(1, 0, 0); *axis = Vector3Of<float>(1, 0, 0);
} }
} }
Quaternion Quaternion::SlerpUnclamped(const Quaternion& a, Quaternion Quaternion::SlerpUnclamped(const Quaternion& a,
@ -298,9 +298,9 @@ Quaternion Quaternion::SlerpUnclamped(const Quaternion& a,
return a; return a;
} }
const Vector3 axyz = a.xyz(); const Vector3Of<float> axyz = a.xyz();
const Vector3 bxyz = b.xyz(); const Vector3Of<float> bxyz = b.xyz();
float cosHalfAngle = a.w * b.w + Vector3::Dot(axyz, bxyz); float cosHalfAngle = a.w * b.w + Vector3Of<float>::Dot(axyz, bxyz);
Quaternion b2 = b; Quaternion b2 = b;
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f) { if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f) {
@ -328,9 +328,9 @@ Quaternion Quaternion::SlerpUnclamped(const Quaternion& a,
blendA = 1.0f - t; blendA = 1.0f - t;
blendB = t; blendB = t;
} }
Vector3 v = axyz * blendA + b2.xyz() * blendB; Vector3Of<float> v = axyz * blendA + b2.xyz() * blendB;
Quaternion result = Quaternion result =
Quaternion(v.Right(), v.Up(), v.Forward(), blendA * a.w + blendB * b2.w); Quaternion(v.horizontal, v.vertical, v.depth, blendA * a.w + blendB * b2.w);
if (result.GetLengthSquared() > 0.0f) if (result.GetLengthSquared() > 0.0f)
return Quaternion::Normalize(result); return Quaternion::Normalize(result);
else else
@ -348,16 +348,16 @@ Quaternion Quaternion::Slerp(const Quaternion& a,
} }
Quaternion Quaternion::Euler(float x, float y, float z) { Quaternion Quaternion::Euler(float x, float y, float z) {
return Quaternion::Euler(Vector3(x, y, z)); return Quaternion::Euler(Vector3Of<float>(x, y, z));
} }
Quaternion Quaternion::Euler(Vector3 euler) { Quaternion Quaternion::Euler(Vector3Of<float> euler) {
return Quaternion::FromEulerRad(euler * Deg2Rad); return Quaternion::FromEulerRad(euler * Deg2Rad);
} }
Quaternion Quaternion::FromEulerRad(Vector3 euler) { Quaternion Quaternion::FromEulerRad(Vector3Of<float> euler) {
float yaw = euler.Right(); float yaw = euler.horizontal;
float pitch = euler.Up(); float pitch = euler.vertical;
float roll = euler.Forward(); float roll = euler.depth;
float rollOver2 = roll * 0.5f; float rollOver2 = roll * 0.5f;
float sinRollOver2 = (float)sin((float)rollOver2); float sinRollOver2 = (float)sin((float)rollOver2);
float cosRollOver2 = (float)cos((float)rollOver2); float cosRollOver2 = (float)cos((float)rollOver2);
@ -380,15 +380,15 @@ Quaternion Quaternion::FromEulerRad(Vector3 euler) {
} }
Quaternion Quaternion::EulerXYZ(float x, float y, float z) { Quaternion Quaternion::EulerXYZ(float x, float y, float z) {
return Quaternion::EulerXYZ(Vector3(x, y, z)); return Quaternion::EulerXYZ(Vector3Of<float>(x, y, z));
} }
Quaternion Quaternion::EulerXYZ(Vector3 euler) { Quaternion Quaternion::EulerXYZ(Vector3Of<float> euler) {
return Quaternion::FromEulerRadXYZ(euler * Deg2Rad); return Quaternion::FromEulerRadXYZ(euler * Deg2Rad);
} }
Quaternion Quaternion::FromEulerRadXYZ(Vector3 euler) { Quaternion Quaternion::FromEulerRadXYZ(Vector3Of<float> euler) {
float yaw = euler.Right(); // x; float yaw = euler.horizontal; // x;
float pitch = euler.Up(); // y; float pitch = euler.vertical; // y;
float roll = euler.Forward(); // z; float roll = euler.depth; // z;
float rollOver2 = roll * 0.5f; float rollOver2 = roll * 0.5f;
float sinRollOver2 = (float)sin((float)rollOver2); float sinRollOver2 = (float)sin((float)rollOver2);
float cosRollOver2 = (float)cos((float)rollOver2); float cosRollOver2 = (float)cos((float)rollOver2);
@ -410,29 +410,29 @@ Quaternion Quaternion::FromEulerRadXYZ(Vector3 euler) {
return result; return result;
} }
float Quaternion::GetAngleAround(Vector3 axis, Quaternion rotation) { float Quaternion::GetAngleAround(Vector3Of<float> axis, Quaternion rotation) {
Quaternion secondaryRotation = GetRotationAround(axis, rotation); Quaternion secondaryRotation = GetRotationAround(axis, rotation);
float rotationAngle; float rotationAngle;
Vector3 rotationAxis; Vector3Of<float> rotationAxis;
secondaryRotation.ToAngleAxis(&rotationAngle, &rotationAxis); secondaryRotation.ToAngleAxis(&rotationAngle, &rotationAxis);
// Do the axis point in opposite directions? // Do the axis point in opposite directions?
if (Vector3::Dot(axis, rotationAxis) < 0) if (Vector3Of<float>::Dot(axis, rotationAxis) < 0)
rotationAngle = -rotationAngle; rotationAngle = -rotationAngle;
return rotationAngle; return rotationAngle;
} }
Quaternion Quaternion::GetRotationAround(Vector3 axis, Quaternion rotation) { Quaternion Quaternion::GetRotationAround(Vector3Of<float> axis, Quaternion rotation) {
Vector3 ra = Vector3(rotation.x, rotation.y, rotation.z); // rotation axis Vector3Of<float> ra = Vector3Of<float>(rotation.x, rotation.y, rotation.z); // rotation axis
Vector3 p = Vector3::Project( Vector3Of<float> p = Vector3Of<float>::Project(
ra, axis); // return projection ra on to axis (parallel component) ra, axis); // return projection ra on to axis (parallel component)
Quaternion twist = Quaternion(p.Right(), p.Up(), p.Forward(), rotation.w); Quaternion twist = Quaternion(p.horizontal, p.vertical, p.depth, rotation.w);
twist = Quaternion::Normalize(twist); twist = Quaternion::Normalize(twist);
return twist; return twist;
} }
void Quaternion::GetSwingTwist(Vector3 axis, void Quaternion::GetSwingTwist(Vector3Of<float> axis,
Quaternion rotation, Quaternion rotation,
Quaternion* swing, Quaternion* swing,
Quaternion* twist) { Quaternion* twist) {

View File

@ -89,7 +89,7 @@ struct Quaternion : Quat {
/// <param name="q">The quaternion to convert</param> /// <param name="q">The quaternion to convert</param>
/// <returns>A vector containing euler angles</returns> /// <returns>A vector containing euler angles</returns>
/// The euler angles performed in the order: Z, X, Y /// The euler angles performed in the order: Z, X, Y
static Vector3 ToAngles(const Quaternion& q); static Vector3Of<float> ToAngles(const Quaternion& q);
Matrix2 ToRotationMatrix(); Matrix2 ToRotationMatrix();
@ -98,7 +98,7 @@ struct Quaternion : Quat {
/// </summary> /// </summary>
/// <param name="vector">The vector to rotate</param> /// <param name="vector">The vector to rotate</param>
/// <returns>The rotated vector</returns> /// <returns>The rotated vector</returns>
Vector3 operator*(const Vector3& vector) const; Vector3Of<float> operator*(const Vector3Of<float>& vector) const;
/// <summary> /// <summary>
/// Multiply this quaternion with another quaternion /// Multiply this quaternion with another quaternion
/// </summary> /// </summary>
@ -132,8 +132,8 @@ struct Quaternion : Quat {
/// <param name="forward">The look direction</param> /// <param name="forward">The look direction</param>
/// <param name="upwards">The up direction</param> /// <param name="upwards">The up direction</param>
/// <returns>The look rotation</returns> /// <returns>The look rotation</returns>
static Quaternion LookRotation(const Vector3& forward, static Quaternion LookRotation(const Vector3Of<float>& forward,
const Vector3& upwards); const Vector3Of<float>& upwards);
/// <summary> /// <summary>
/// Creates a quaternion with the given forward direction with up = /// Creates a quaternion with the given forward direction with up =
/// Vector3::up /// Vector3::up
@ -143,7 +143,7 @@ struct Quaternion : Quat {
/// For the rotation, Vector::up is used for the up direction. /// For the rotation, Vector::up is used for the up direction.
/// Note: if the forward direction == Vector3::up, the result is /// Note: if the forward direction == Vector3::up, the result is
/// Quaternion::identity /// Quaternion::identity
static Quaternion LookRotation(const Vector3& forward); static Quaternion LookRotation(const Vector3Of<float>& forward);
/// <summary> /// <summary>
/// Calculat the rotation from on vector to another /// Calculat the rotation from on vector to another
@ -151,7 +151,7 @@ struct Quaternion : Quat {
/// <param name="fromDirection">The from direction</param> /// <param name="fromDirection">The from direction</param>
/// <param name="toDirection">The to direction</param> /// <param name="toDirection">The to direction</param>
/// <returns>The rotation from the first to the second vector</returns> /// <returns>The rotation from the first to the second vector</returns>
static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection); static Quaternion FromToRotation(Vector3Of<float> fromDirection, Vector3Of<float> toDirection);
/// <summary> /// <summary>
/// Rotate form one orientation to anther with a maximum amount of degrees /// Rotate form one orientation to anther with a maximum amount of degrees
@ -170,13 +170,13 @@ struct Quaternion : Quat {
/// <param name="angle">The angle</param> /// <param name="angle">The angle</param>
/// <param name="axis">The axis</param> /// <param name="axis">The axis</param>
/// <returns>The resulting quaternion</returns> /// <returns>The resulting quaternion</returns>
static Quaternion AngleAxis(float angle, const Vector3& axis); static Quaternion AngleAxis(float angle, const Vector3Of<float>& axis);
/// <summary> /// <summary>
/// Convert this quaternion to angle/axis representation /// Convert this quaternion to angle/axis representation
/// </summary> /// </summary>
/// <param name="angle">A pointer to the angle for the result</param> /// <param name="angle">A pointer to the angle for the result</param>
/// <param name="axis">A pointer to the axis for the result</param> /// <param name="axis">A pointer to the axis for the result</param>
void ToAngleAxis(float* angle, Vector3* axis); void ToAngleAxis(float* angle, Vector3Of<float>* axis);
/// <summary> /// <summary>
/// Get the angle between two orientations /// Get the angle between two orientations
@ -225,7 +225,7 @@ struct Quaternion : Quat {
/// <param name="eulerAngles">Vector with the euler angles</param> /// <param name="eulerAngles">Vector with the euler angles</param>
/// <returns>The resulting quaternion</returns> /// <returns>The resulting quaternion</returns>
/// Rotation are appied in the order Z, X, Y. /// Rotation are appied in the order Z, X, Y.
static Quaternion Euler(Vector3 eulerAngles); static Quaternion Euler(Vector3Of<float> eulerAngles);
/// <summary> /// <summary>
/// Create a rotation from euler angles /// Create a rotation from euler angles
@ -242,7 +242,7 @@ struct Quaternion : Quat {
/// <param name="eulerAngles">Vector with the euler angles</param> /// <param name="eulerAngles">Vector with the euler angles</param>
/// <returns>The resulting quaternion</returns> /// <returns>The resulting quaternion</returns>
/// Rotation are appied in the order X, Y, Z. /// Rotation are appied in the order X, Y, Z.
static Quaternion EulerXYZ(Vector3 eulerAngles); static Quaternion EulerXYZ(Vector3Of<float> eulerAngles);
/// <summary> /// <summary>
/// Returns the angle of around the give axis for a rotation /// Returns the angle of around the give axis for a rotation
@ -250,14 +250,14 @@ struct Quaternion : Quat {
/// <param name="axis">The axis around which the angle should be /// <param name="axis">The axis around which the angle should be
/// computed</param> <param name="rotation">The source rotation</param> /// computed</param> <param name="rotation">The source rotation</param>
/// <returns>The signed angle around the axis</returns> /// <returns>The signed angle around the axis</returns>
static float GetAngleAround(Vector3 axis, Quaternion rotation); static float GetAngleAround(Vector3Of<float> axis, Quaternion rotation);
/// <summary> /// <summary>
/// Returns the rotation limited around the given axis /// Returns the rotation limited around the given axis
/// </summary> /// </summary>
/// <param name="axis">The axis which which the rotation should be /// <param name="axis">The axis which which the rotation should be
/// limited</param> <param name="rotation">The source rotation</param> /// limited</param> <param name="rotation">The source rotation</param>
/// <returns>The rotation around the given axis</returns> /// <returns>The rotation around the given axis</returns>
static Quaternion GetRotationAround(Vector3 axis, Quaternion rotation); static Quaternion GetRotationAround(Vector3Of<float> axis, Quaternion rotation);
/// <summary> /// <summary>
/// Swing-twist decomposition of a rotation /// Swing-twist decomposition of a rotation
/// </summary> /// </summary>
@ -266,7 +266,7 @@ struct Quaternion : Quat {
/// <param name="swing">A pointer to the quaternion for the swing /// <param name="swing">A pointer to the quaternion for the swing
/// result</param> <param name="twist">A pointer to the quaternion for the /// result</param> <param name="twist">A pointer to the quaternion for the
/// twist result</param> /// twist result</param>
static void GetSwingTwist(Vector3 axis, static void GetSwingTwist(Vector3Of<float> axis,
Quaternion rotation, Quaternion rotation,
Quaternion* swing, Quaternion* swing,
Quaternion* twist); Quaternion* twist);
@ -284,11 +284,11 @@ struct Quaternion : Quat {
float GetLengthSquared() const; float GetLengthSquared() const;
static float GetLengthSquared(const Quaternion& q); static float GetLengthSquared(const Quaternion& q);
void ToAxisAngleRad(const Quaternion& q, Vector3* const axis, float* angle); void ToAxisAngleRad(const Quaternion& q, Vector3Of<float>* const axis, float* angle);
static Quaternion FromEulerRad(Vector3 euler); static Quaternion FromEulerRad(Vector3Of<float> euler);
static Quaternion FromEulerRadXYZ(Vector3 euler); static Quaternion FromEulerRadXYZ(Vector3Of<float> euler);
Vector3 xyz() const; Vector3Of<float> xyz() const;
}; };
} // namespace LinearAlgebra } // namespace LinearAlgebra

View File

@ -64,15 +64,15 @@ SphericalOf<T> SphericalOf<T>::FromPolar(PolarOf<T> polar) {
} }
template <typename T> template <typename T>
SphericalOf<T> SphericalOf<T>::FromVector3(Vector3 v) { SphericalOf<T> SphericalOf<T>::FromVector3(Vector3Of<float> v) {
float distance = v.magnitude(); float distance = v.Magnitude();
if (distance == 0.0f) { if (distance == 0.0f) {
return SphericalOf(distance, AngleOf<T>(), AngleOf<T>()); return SphericalOf(distance, AngleOf<T>(), AngleOf<T>());
} else { } else {
AngleOf<T> verticalAngle = AngleOf<T> verticalAngle =
AngleOf<T>::Radians((pi / 2 - acosf(v.Up() / distance))); AngleOf<T>::Radians((pi / 2 - acosf(v.vertical / distance)));
AngleOf<T> horizontalAngle = AngleOf<T> horizontalAngle =
AngleOf<T>::Radians(atan2f(v.Right(), v.Forward())); AngleOf<T>::Radians(atan2f(v.horizontal, v.depth));
return SphericalOf(distance, horizontalAngle, verticalAngle); return SphericalOf(distance, horizontalAngle, verticalAngle);
} }
} }
@ -89,7 +89,7 @@ SphericalOf<T> SphericalOf<T>::FromVector3(Vector3 v) {
* @return Vector3 The 3D vector representation of the spherical coordinates. * @return Vector3 The 3D vector representation of the spherical coordinates.
*/ */
template <typename T> template <typename T>
Vector3 SphericalOf<T>::ToVector3() const { Vector3Of<float> SphericalOf<T>::ToVector3() const {
float verticalRad = (pi / 2) - this->direction.vertical.InRadians(); float verticalRad = (pi / 2) - this->direction.vertical.InRadians();
float horizontalRad = this->direction.horizontal.InRadians(); float horizontalRad = this->direction.horizontal.InRadians();
@ -102,7 +102,7 @@ Vector3 SphericalOf<T>::ToVector3() const {
float y = this->distance * cosVertical; float y = this->distance * cosVertical;
float z = this->distance * sinVertical * cosHorizontal; float z = this->distance * sinVertical * cosHorizontal;
Vector3 v = Vector3(x, y, z); Vector3Of<float> v = Vector3Of<float>(x, y, z);
return v; return v;
} }
@ -145,9 +145,9 @@ SphericalOf<T> SphericalOf<T>::operator-() const {
template <typename T> template <typename T>
SphericalOf<T> SphericalOf<T>::operator-(const SphericalOf<T>& s2) const { SphericalOf<T> SphericalOf<T>::operator-(const SphericalOf<T>& s2) const {
// let's do it the easy way... // let's do it the easy way...
Vector3 v1 = this->ToVector3(); Vector3Of<float> v1 = this->ToVector3();
Vector3 v2 = s2.ToVector3(); Vector3Of<float> v2 = s2.ToVector3();
Vector3 v = v1 - v2; Vector3Of<float> v = v1 - v2;
SphericalOf<T> r = SphericalOf<T>::FromVector3(v); SphericalOf<T> r = SphericalOf<T>::FromVector3(v);
return r; return r;
} }
@ -160,9 +160,9 @@ SphericalOf<T> SphericalOf<T>::operator-=(const SphericalOf<T>& v) {
template <typename T> template <typename T>
SphericalOf<T> SphericalOf<T>::operator+(const SphericalOf<T>& s2) const { SphericalOf<T> SphericalOf<T>::operator+(const SphericalOf<T>& s2) const {
// let's do it the easy way... // let's do it the easy way...
Vector3 v1 = this->ToVector3(); Vector3Of<float> v1 = this->ToVector3();
Vector3 v2 = s2.ToVector3(); Vector3Of<float> v2 = s2.ToVector3();
Vector3 v = v1 + v2; Vector3Of<float> v = v1 + v2;
SphericalOf<T> r = SphericalOf<T>::FromVector3(v); SphericalOf<T> r = SphericalOf<T>::FromVector3(v);
return r; return r;
/* /*
@ -240,9 +240,9 @@ float SphericalOf<T>::DistanceBetween(const SphericalOf<T>& v1,
const SphericalOf<T>& v2) { const SphericalOf<T>& v2) {
// SphericalOf<T> difference = v1 - v2; // SphericalOf<T> difference = v1 - v2;
// return difference.distance; // return difference.distance;
Vector3 vec1 = v1.ToVector3(); Vector3Of<float> vec1 = v1.ToVector3();
Vector3 vec2 = v2.ToVector3(); Vector3Of<float> vec2 = v2.ToVector3();
float distance = Vector3::Distance(vec1, vec2); float distance = Vector3Of<float>::Distance(vec1, vec2);
return distance; return distance;
} }
@ -253,8 +253,8 @@ AngleOf<T> SphericalOf<T>::AngleBetween(const SphericalOf& v1,
// if (denominator < epsilon) // if (denominator < epsilon)
// return 0.0f; // return 0.0f;
Vector3 v1_3 = v1.ToVector3(); Vector3Of<float> v1_3 = v1.ToVector3();
Vector3 v2_3 = v2.ToVector3(); Vector3Of<float> v2_3 = v2.ToVector3();
// float dot = Vector3::Dot(v1_3, v2_3); // float dot = Vector3::Dot(v1_3, v2_3);
// float fraction = dot / denominator; // float fraction = dot / denominator;
// if (isnan(fraction)) // if (isnan(fraction))
@ -262,7 +262,7 @@ AngleOf<T> SphericalOf<T>::AngleBetween(const SphericalOf& v1,
// float cdot = Float::Clamp(fraction, -1.0, 1.0); // float cdot = Float::Clamp(fraction, -1.0, 1.0);
// float r = ((float)acos(cdot)) * Rad2Deg; // float r = ((float)acos(cdot)) * Rad2Deg;
AngleSingle r = Vector3::Angle(v1_3, v2_3); AngleSingle r = Vector3Of<float>::UnsignedAngle(v1_3, v2_3);
return AngleOf<T>::Degrees(r.InDegrees()); return AngleOf<T>::Degrees(r.InDegrees());
} }
@ -270,10 +270,10 @@ template <typename T>
AngleOf<T> SphericalOf<T>::SignedAngleBetween(const SphericalOf<T>& v1, AngleOf<T> SphericalOf<T>::SignedAngleBetween(const SphericalOf<T>& v1,
const SphericalOf<T>& v2, const SphericalOf<T>& v2,
const SphericalOf<T>& axis) { const SphericalOf<T>& axis) {
Vector3 v1_vector = v1.ToVector3(); Vector3Of<float> v1_vector = v1.ToVector3();
Vector3 v2_vector = v2.ToVector3(); Vector3Of<float> v2_vector = v2.ToVector3();
Vector3 axis_vector = axis.ToVector3(); Vector3Of<float> axis_vector = axis.ToVector3();
AngleSingle r = Vector3::SignedAngle(v1_vector, v2_vector, axis_vector); AngleSingle r = Vector3Of<float>::SignedAngle(v1_vector, v2_vector, axis_vector);
return AngleOf<T>::Degrees(r.InDegrees()); return AngleOf<T>::Degrees(r.InDegrees());
} }

View File

@ -59,10 +59,10 @@ class SphericalOf {
/// @brief Create a Spherical coordinate from a Vector3 coordinate /// @brief Create a Spherical coordinate from a Vector3 coordinate
/// @param v The vector coordinate /// @param v The vector coordinate
/// @return The spherical coordinate /// @return The spherical coordinate
static SphericalOf<T> FromVector3(Vector3 v); static SphericalOf<T> FromVector3(Vector3Of<float> v);
/// @brief Convert the spherical coordinate to a Vector3 coordinate /// @brief Convert the spherical coordinate to a Vector3 coordinate
/// @return The vector coordinate /// @return The vector coordinate
Vector3 ToVector3() const; Vector3Of<float> ToVector3() const;
/// @brief A spherical vector with zero degree angles and distance /// @brief A spherical vector with zero degree angles and distance
const static SphericalOf<T> zero; const static SphericalOf<T> zero;

View File

@ -69,9 +69,9 @@ Quaternion SwingTwistOf<T>::ToQuaternion() const {
template <typename T> template <typename T>
SwingTwistOf<T> SwingTwistOf<T>::FromQuaternion(Quaternion q) { SwingTwistOf<T> SwingTwistOf<T>::FromQuaternion(Quaternion q) {
Vector3 angles = Quaternion::ToAngles(q); Vector3Of<float> angles = Quaternion::ToAngles(q);
SwingTwistOf<T> r = SwingTwistOf<T> r =
SwingTwistOf<T>::Degrees(angles.Up(), angles.Right(), angles.Forward()); SwingTwistOf<T>::Degrees(angles.vertical, angles.horizontal, angles.depth);
r.Normalize(); r.Normalize();
return r; return r;
} }
@ -80,7 +80,7 @@ template <typename T>
SphericalOf<T> SwingTwistOf<T>::ToAngleAxis() const { SphericalOf<T> SwingTwistOf<T>::ToAngleAxis() const {
Quaternion q = this->ToQuaternion(); Quaternion q = this->ToQuaternion();
float angle; float angle;
Vector3 axis; Vector3Of<float> axis;
q.ToAngleAxis(&angle, &axis); q.ToAngleAxis(&angle, &axis);
DirectionOf<T> direction = DirectionOf<T>::FromVector3(axis); DirectionOf<T> direction = DirectionOf<T>::FromVector3(axis);
@ -90,7 +90,7 @@ SphericalOf<T> SwingTwistOf<T>::ToAngleAxis() const {
template <typename T> template <typename T>
SwingTwistOf<T> SwingTwistOf<T>::FromAngleAxis(SphericalOf<T> aa) { SwingTwistOf<T> SwingTwistOf<T>::FromAngleAxis(SphericalOf<T> aa) {
Vector3 vectorAxis = aa.direction.ToVector3(); Vector3Of<float> vectorAxis = aa.direction.ToVector3();
Quaternion q = Quaternion::AngleAxis(aa.distance, vectorAxis); Quaternion q = Quaternion::AngleAxis(aa.distance, vectorAxis);
return SwingTwistOf<T>(); return SwingTwistOf<T>();
} }
@ -139,7 +139,7 @@ SwingTwistOf<T> SwingTwistOf<T>::Inverse(SwingTwistOf<T> rotation) {
template <typename T> template <typename T>
SwingTwistOf<T> SwingTwistOf<T>::AngleAxis(float angle, SwingTwistOf<T> SwingTwistOf<T>::AngleAxis(float angle,
const DirectionOf<T>& axis) { const DirectionOf<T>& axis) {
Vector3 axis_vector = axis.ToVector3(); Vector3Of<float> axis_vector = axis.ToVector3();
Quaternion q = Quaternion::AngleAxis(angle, axis_vector); Quaternion q = Quaternion::AngleAxis(angle, axis_vector);
SwingTwistOf<T> r = SwingTwistOf<T>::FromQuaternion(q); SwingTwistOf<T> r = SwingTwistOf<T>::FromQuaternion(q);
return r; return r;

View File

@ -7,181 +7,7 @@
#include "FloatSingle.h" #include "FloatSingle.h"
#include "Vector3.h" #include "Vector3.h"
// #if defined(AVR)
// #include <Arduino.h>
// #else
#include <math.h> #include <math.h>
// #endif
/*
Vector2::Vector2() {
x = 0;
y = 0;
}
Vector2::Vector2(float _x, float _y) {
x = _x;
y = _y;
}
// Vector2::Vector2(Vec2 v) {
// x = v.x;
// y = v.y;
// }
Vector2::Vector2(Vector3 v) {
x = v.Right(); // x;
y = v.Forward(); // z;
}
Vector2::Vector2(PolarSingle p) {
float horizontalRad = p.angle.InDegrees() * Deg2Rad;
float cosHorizontal = cosf(horizontalRad);
float sinHorizontal = sinf(horizontalRad);
x = p.distance * sinHorizontal;
y = p.distance * cosHorizontal;
}
Vector2::~Vector2() {}
const Vector2 Vector2::zero = Vector2(0, 0);
const Vector2 Vector2::one = Vector2(1, 1);
const Vector2 Vector2::right = Vector2(1, 0);
const Vector2 Vector2::left = Vector2(-1, 0);
const Vector2 Vector2::up = Vector2(0, 1);
const Vector2 Vector2::down = Vector2(0, -1);
const Vector2 Vector2::forward = Vector2(0, 1);
const Vector2 Vector2::back = Vector2(0, -1);
bool Vector2::operator==(const Vector2& v) {
return (this->x == v.x && this->y == v.y);
}
float Vector2::Magnitude(const Vector2& v) {
return sqrtf(v.x * v.x + v.y * v.y);
}
float Vector2::magnitude() const {
return (float)sqrtf(x * x + y * y);
}
float Vector2::SqrMagnitude(const Vector2& v) {
return v.x * v.x + v.y * v.y;
}
float Vector2::sqrMagnitude() const {
return (x * x + y * y);
}
Vector2 Vector2::Normalize(const Vector2& v) {
float num = Vector2::Magnitude(v);
Vector2 result = Vector2::zero;
if (num > Float::epsilon) {
result = v / num;
}
return result;
}
Vector2 Vector2::normalized() const {
float num = this->magnitude();
Vector2 result = Vector2::zero;
if (num > Float::epsilon) {
result = ((Vector2) * this) / num;
}
return result;
}
Vector2 Vector2::operator-() {
return Vector2(-this->x, -this->y);
}
Vector2 Vector2::operator-(const Vector2& v) const {
return Vector2(this->x - v.x, this->y - v.y);
}
Vector2 Vector2::operator-=(const Vector2& v) {
this->x -= v.x;
this->y -= v.y;
return *this;
}
Vector2 Vector2::operator+(const Vector2& v) const {
return Vector2(this->x + v.x, this->y + v.y);
}
Vector2 Vector2::operator+=(const Vector2& v) {
this->x += v.x;
this->y += v.y;
return *this;
}
Vector2 Vector2::Scale(const Vector2& v1, const Vector2& v2) {
return Vector2(v1.x * v2.x, v1.y * v2.y);
}
// Vector2 Passer::LinearAlgebra::operator*(const Vector2 &v, float f) {
// return Vector2(v.x * f, v.y * f);
// }
// Vector2 Passer::LinearAlgebra::operator*(float f, const Vector2 &v) {
// return Vector2(v.x * f, v.y * f);
// }
Vector2 Vector2::operator*=(float f) {
this->x *= f;
this->y *= f;
return *this;
}
// Vector2 Passer::LinearAlgebra::operator/(const Vector2 &v, float f) {
// return Vector2(v.x / f, v.y / f);
// }
// Vector2 Passer::LinearAlgebra::operator/(float f, const Vector2 &v) {
// return Vector2(v.x / f, v.y / f);
// }
Vector2 Vector2::operator/=(float f) {
this->x /= f;
this->y /= f;
return *this;
}
float Vector2::Dot(const Vector2& v1, const Vector2& v2) {
return v1.x * v2.x + v1.y * v2.y;
}
float Vector2::Distance(const Vector2& v1, const Vector2& v2) {
return Magnitude(v1 - v2);
}
float Vector2::Angle(const Vector2& v1, const Vector2& v2) {
return (float)fabs(SignedAngle(v1, v2));
}
float Vector2::SignedAngle(const Vector2& v1, const Vector2& v2) {
float sqrMagFrom = v1.sqrMagnitude();
float sqrMagTo = v2.sqrMagnitude();
if (sqrMagFrom == 0 || sqrMagTo == 0)
return 0;
if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo))
#if defined(AVR)
return NAN;
#else
return nanf("");
#endif
float angleFrom = atan2f(v1.y, v1.x);
float angleTo = atan2f(v2.y, v2.x);
return -(angleTo - angleFrom) * Rad2Deg;
}
Vector2 Vector2::Rotate(const Vector2& v, AngleSingle a) {
float angleRad = a.InDegrees() * Deg2Rad;
#if defined(AVR)
float sinValue = sin(angleRad);
float cosValue = cos(angleRad); // * Angle::Deg2Rad);
#else
float sinValue = (float)sinf(angleRad);
float cosValue = (float)cosf(angleRad);
#endif
float tx = v.x;
float ty = v.y;
Vector2 r = Vector2((cosValue * tx) - (sinValue * ty),
(sinValue * tx) + (cosValue * ty));
return r;
}
Vector2 Vector2::Lerp(const Vector2& v1, const Vector2& v2, float f) {
Vector2 v = v1 + (v2 - v1) * f;
return v;
}
*/
#pragma region Vector2Of #pragma region Vector2Of
@ -193,10 +19,9 @@ Vector2Of<T>::Vector2Of(T horizontal, T vertical)
: horizontal(horizontal), vertical(vertical) {} : horizontal(horizontal), vertical(vertical) {}
template <typename T> template <typename T>
Vector2Of<float> Vector2Of<T>::FromPolar(PolarOf<T> p) { Vector2Of<float> Vector2Of<T>::FromPolar(PolarOf<float> p) {
float horizontalRad = p.angle.InDegrees() * Deg2Rad; float cosHorizontal = AngleOf<float>::Cos(p.angle);
float cosHorizontal = cosf(horizontalRad); float sinHorizontal = AngleOf<float>::Sin(p.angle);
float sinHorizontal = sinf(horizontalRad);
Vector2Of<float> v; Vector2Of<float> v;
v.horizontal = p.distance * sinHorizontal; v.horizontal = p.distance * sinHorizontal;
@ -226,7 +51,8 @@ template <typename T>
float Vector2Of<T>::Magnitude() const { float Vector2Of<T>::Magnitude() const {
T sqr = this->horizontal * this->horizontal + this->vertical * this->vertical; T sqr = this->horizontal * this->horizontal + this->vertical * this->vertical;
float sqrFloat = static_cast<float>(sqr); float sqrFloat = static_cast<float>(sqr);
return sqrtf(sqrFloat); float r = sqrtf(sqrFloat);
return r;
} }
template <typename T> template <typename T>
@ -241,6 +67,13 @@ float Vector2Of<T>::SqrMagnitude() const {
return static_cast<float>(sqr); return static_cast<float>(sqr);
} }
template <typename T>
float Vector2Of<T>::Distance(const Vector2Of& v1, const Vector2Of& v2) {
Vector2Of<float> delta = v1 - v2;
float r = MagnitudeOf(delta);
return r;
}
template <typename T> template <typename T>
Vector2Of<T> Vector2Of<T>::operator-() { Vector2Of<T> Vector2Of<T>::operator-() {
return Vector2Of<T>(-this->horizontal, -this->vertical); return Vector2Of<T>(-this->horizontal, -this->vertical);
@ -289,11 +122,6 @@ T Vector2Of<T>::Dot(const Vector2Of& v1, const Vector2Of& v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical; return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical;
} }
template <typename T>
float Vector2Of<T>::Distance(const Vector2Of& v1, const Vector2Of& v2) {
return MagnitudeOf(v1 - v2);
}
template <typename T> template <typename T>
Vector2Of<float> Vector2Of<T>::Normalize(const Vector2Of<T>& v) { Vector2Of<float> Vector2Of<T>::Normalize(const Vector2Of<T>& v) {
float num = Vector2Of<T>::MagnitudeOf(v); float num = Vector2Of<T>::MagnitudeOf(v);
@ -305,32 +133,33 @@ Vector2Of<float> Vector2Of<T>::Normalize(const Vector2Of<T>& v) {
} }
template <typename T> template <typename T>
AngleOf<T> Vector2Of<T>::UnsignedAngle(const Vector2Of& v1, AngleOf<float> Vector2Of<T>::UnsignedAngle(const Vector2Of& v1,
const Vector2Of& v2) { const Vector2Of& v2) {
return AngleOf<T>::Abs(SignedAngle(v1, v2)); return AngleOf<float>::Abs(SignedAngle(v1, v2));
} }
template <typename T> template <typename T>
AngleOf<T> Vector2Of<T>::SignedAngle(const Vector2Of& v1, const Vector2Of& v2) { AngleOf<float> Vector2Of<T>::SignedAngle(const Vector2Of& v1,
const Vector2Of& v2) {
float sqrMagFrom = v1.SqrMagnitude(); float sqrMagFrom = v1.SqrMagnitude();
float sqrMagTo = v2.SqrMagnitude(); float sqrMagTo = v2.SqrMagnitude();
if (sqrMagFrom == 0 || sqrMagTo == 0) if (sqrMagFrom == 0 || sqrMagTo == 0)
return AngleOf<T>::zero; return AngleOf<float>::zero;
if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo)) if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo))
return AngleOf<T>::zero; // Angle does not support NaN... return AngleOf<float>::zero; // Angle does not support NaN...
AngleOf<T> angleFrom = AngleOf<T>::Atan2(static_cast<float>(v1.vertical), AngleOf<float> angleFrom = AngleOf<float>::Atan2(
static_cast<float>(v1.horizontal)); static_cast<float>(v1.vertical), static_cast<float>(v1.horizontal));
AngleOf<T> angleTo = AngleOf<T>::Atan2(static_cast<float>(v2.vertical), AngleOf<float> angleTo = AngleOf<float>::Atan2(
static_cast<float>(v2.horizontal)); static_cast<float>(v2.vertical), static_cast<float>(v2.horizontal));
return -(angleTo - angleFrom); return -(angleTo - angleFrom);
} }
template <typename T> template <typename T>
Vector2Of<float> Vector2Of<T>::Rotate(const Vector2Of& v, AngleOf<T> a) { Vector2Of<float> Vector2Of<T>::Rotate(const Vector2Of& v, AngleOf<float> a) {
float sinValue = AngleOf<T>::Sin(a); float sinValue = AngleOf<float>::Sin(a);
float cosValue = AngleOf<T>::Cos(a); float cosValue = AngleOf<float>::Cos(a);
float tx = static_cast<float>(v.horizontal); float tx = static_cast<float>(v.horizontal);
float ty = static_cast<float>(v.vertical); float ty = static_cast<float>(v.vertical);
Vector2Of<float> r = Vector2Of<float>((cosValue * tx) - (sinValue * ty), Vector2Of<float> r = Vector2Of<float>((cosValue * tx) - (sinValue * ty),
@ -342,31 +171,12 @@ template <typename T>
Vector2Of<float> Vector2Of<T>::Lerp(const Vector2Of& v1, Vector2Of<float> Vector2Of<T>::Lerp(const Vector2Of& v1,
const Vector2Of& v2, const Vector2Of& v2,
float f) { float f) {
Vector2Of<float> v = v1 + static_cast<Vector2Of<float>>(v2 - v1) * f; Vector2Of<float> v1f = (Vector2Of<float>)v1;
return v; Vector2Of<float> delta = v2 - v1;
Vector2Of<float> r = v1f + delta * f;
return r;
} }
// float Vector2::Angle(const Vector2& v1, const Vector2& v2) {
// return (float)fabs(SignedAngle(v1, v2));
// }
// float Vector2::SignedAngle(const Vector2& v1, const Vector2& v2) {
// float sqrMagFrom = v1.sqrMagnitude();
// float sqrMagTo = v2.sqrMagnitude();
// if (sqrMagFrom == 0 || sqrMagTo == 0)
// return 0;
// if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo))
// #if defined(AVR)
// return NAN;
// #else
// return nanf("");
// #endif
// float angleFrom = atan2f(v1.y, v1.x);
// float angleTo = atan2f(v2.y, v2.x);
// return -(angleTo - angleFrom) * Rad2Deg;
// }
template <typename T> template <typename T>
Vector2Of<float> Vector2Of<T>::Normalized() const { Vector2Of<float> Vector2Of<T>::Normalized() const {
float num = Vector2Of<T>::MagnitudeOf(*this); float num = Vector2Of<T>::MagnitudeOf(*this);
@ -379,6 +189,7 @@ Vector2Of<float> Vector2Of<T>::Normalized() const {
// Explicit instantiation for int // Explicit instantiation for int
template class Vector2Of<signed short>; template class Vector2Of<signed short>;
template class Vector2Of<int>;
template class Vector2Of<float>; template class Vector2Of<float>;
#pragma endregion Vector2Of #pragma endregion Vector2Of

View File

@ -216,8 +216,6 @@ class Vector2Of {
/// up = positive /// up = positive
Vector2Of(T horizontal, T vertical); Vector2Of(T horizontal, T vertical);
static Vector2Of<float> FromPolar(PolarOf<T> v);
/// @brief Converting constructor: allow Vector2Of<U> -> Vector2Of<T> /// @brief Converting constructor: allow Vector2Of<U> -> Vector2Of<T>
/// @tparam U /// @tparam U
/// @param other /// @param other
@ -226,10 +224,13 @@ class Vector2Of {
: horizontal(static_cast<T>(other.horizontal)), : horizontal(static_cast<T>(other.horizontal)),
vertical(static_cast<T>(other.vertical)) {} vertical(static_cast<T>(other.vertical)) {}
static Vector2Of<float> FromPolar(PolarOf<float> v);
template <typename U> template <typename U>
constexpr Vector2Of& operator=(const Vector2Of<U>& other) noexcept { constexpr Vector2Of& operator=(const Vector2Of<U>& other) noexcept {
this.horizontal = static_cast<T>(other.horizontal); this->horizontal = static_cast<T>(other.horizontal);
this.vertical = static_cast<T>(other.vertical); this->vertical = static_cast<T>(other.vertical);
return *this;
} }
/// @brief A vector with zero for all axis /// @brief A vector with zero for all axis
@ -266,6 +267,12 @@ class Vector2Of {
/// of the squared root of C. /// of the squared root of C.
float SqrMagnitude() const; float SqrMagnitude() const;
/// @brief The distance between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The distance between the two vectors
static float Distance(const Vector2Of& v1, const Vector2Of& v2);
/// @brief Negate the vector such that it points in the opposite direction /// @brief Negate the vector such that it points in the opposite direction
/// @return The negated vector /// @return The negated vector
Vector2Of operator-(); Vector2Of operator-();
@ -315,12 +322,6 @@ class Vector2Of {
/// @return The dot product of the two vectors /// @return The dot product of the two vectors
static T Dot(const Vector2Of& v1, const Vector2Of& v2); static T Dot(const Vector2Of& v1, const Vector2Of& v2);
/// @brief The distance between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The distance between the two vectors
static float Distance(const Vector2Of& v1, const Vector2Of& v2);
static Vector2Of<float> Normalize(const Vector2Of& v); static Vector2Of<float> Normalize(const Vector2Of& v);
/// @brief Convert the vector to a length 1 /// @brief Convert the vector to a length 1
/// @return The vector normalized to a length of 1 /// @return The vector normalized to a length of 1
@ -333,18 +334,18 @@ class Vector2Of {
/// @remark This reterns an unsigned angle which is the shortest distance /// @remark This reterns an unsigned angle which is the shortest distance
/// between the two vectors. Use Vector2::SignedAngle if a signed angle is /// between the two vectors. Use Vector2::SignedAngle if a signed angle is
/// needed. /// needed.
static AngleOf<T> UnsignedAngle(const Vector2Of& v1, const Vector2Of& v2); static AngleOf<float> UnsignedAngle(const Vector2Of& v1, const Vector2Of& v2);
/// @brief The signed angle between two vectors /// @brief The signed angle between two vectors
/// @param v1 The starting vector /// @param v1 The starting vector
/// @param v2 The ending vector /// @param v2 The ending vector
/// @return The signed angle between the two vectors /// @return The signed angle between the two vectors
static AngleOf<T> SignedAngle(const Vector2Of& v1, const Vector2Of& v2); static AngleOf<float> SignedAngle(const Vector2Of& v1, const Vector2Of& v2);
/// @brief Rotate the vector /// @brief Rotate the vector
/// @param v The vector to rotate /// @param v The vector to rotate
/// @param a The angle in degrees to rotate /// @param a The angle in degrees to rotate
/// @return The rotated vector /// @return The rotated vector
static Vector2Of<float> Rotate(const Vector2Of& v, AngleOf<T> a); static Vector2Of<float> Rotate(const Vector2Of& v, AngleOf<float> a);
/// @brief Lerp (linear interpolation) between two vectors /// @brief Lerp (linear interpolation) between two vectors
/// @param v1 The starting vector /// @param v1 The starting vector
@ -354,7 +355,9 @@ class Vector2Of {
/// @remark The factor f is unclamped. Value 0 matches the vector *v1*, Value /// @remark The factor f is unclamped. Value 0 matches the vector *v1*, Value
/// 1 matches vector *v2*. Value -1 is vector *v1* minus the difference /// 1 matches vector *v2*. Value -1 is vector *v1* minus the difference
/// between *v1* and *v2* etc. /// between *v1* and *v2* etc.
static Vector2Of<float> Lerp(const Vector2Of& v1, const Vector2Of& v2, float f); static Vector2Of<float> Lerp(const Vector2Of& v1,
const Vector2Of& v2,
float f);
}; };
#pragma region friend functions #pragma region friend functions

View File

@ -4,6 +4,7 @@
#include "Vector3.h" #include "Vector3.h"
#include "Angle.h" #include "Angle.h"
#include "FloatSingle.h"
#include "Spherical.h" #include "Spherical.h"
// #include "TypeTraits.h" // #include "TypeTraits.h"
@ -13,6 +14,7 @@ const float Deg2Rad = 0.0174532924F;
const float Rad2Deg = 57.29578F; const float Rad2Deg = 57.29578F;
const float epsilon = 1E-05f; const float epsilon = 1E-05f;
/*
Vector3::Vector3() { Vector3::Vector3() {
this->x = 0; this->x = 0;
this->y = 0; this->y = 0;
@ -161,7 +163,8 @@ float Vector3::Distance(const Vector3& v1, const Vector3& v2) {
} }
Vector3 Vector3::Cross(const Vector3& v1, const Vector3& v2) { Vector3 Vector3::Cross(const Vector3& v1, const Vector3& v2) {
return Vector3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); return Vector3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x *
v2.y - v1.y * v2.x);
} }
Vector3 Vector3::Project(const Vector3& v, const Vector3& n) { Vector3 Vector3::Project(const Vector3& v, const Vector3& n) {
@ -194,14 +197,16 @@ AngleOf<float> Vector3::Angle(const Vector3& v1, const Vector3& v2) {
float dot = Vector3::Dot(v1, v2); float dot = Vector3::Dot(v1, v2);
float fraction = dot / denominator; float fraction = dot / denominator;
if (isnan(fraction)) if (isnan(fraction))
return AngleOf<float>::Degrees(fraction); // short cut to returning NaN universally return AngleOf<float>::Degrees(fraction); // short cut to returning NaN
universally
float cdot = clamp(fraction, -1.0, 1.0); float cdot = clamp(fraction, -1.0, 1.0);
float r = ((float)acos(cdot)); float r = ((float)acos(cdot));
return AngleOf<float>::Radians(r); return AngleOf<float>::Radians(r);
} }
AngleOf<float> Vector3::SignedAngle(const Vector3& v1, const Vector3& v2, const Vector3& axis) { AngleOf<float> Vector3::SignedAngle(const Vector3& v1, const Vector3& v2, const
Vector3& axis) {
// angle in [0,180] // angle in [0,180]
AngleOf<float> angle = Vector3::Angle(v1, v2); AngleOf<float> angle = Vector3::Angle(v1, v2);
@ -219,6 +224,7 @@ Vector3 Vector3::Lerp(const Vector3& v1, const Vector3& v2, float f) {
Vector3 v = v1 + (v2 - v1) * f; Vector3 v = v1 + (v2 - v1) * f;
return v; return v;
} }
*/
#pragma region Vector3Of #pragma region Vector3Of
@ -232,44 +238,47 @@ Vector3Of<T>::Vector3Of(T horizontal, T vertical, T depth)
: horizontal(horizontal), vertical(vertical), depth(depth) {} : horizontal(horizontal), vertical(vertical), depth(depth) {}
template <typename T> template <typename T>
Vector3Of<T>::Vector3Of(Vector2Of<T> v) : horizontal(v.horizontal), vertical(v.vertical) {} Vector3Of<T>::Vector3Of(Vector2Of<T> v)
: horizontal(v.horizontal), vertical(v.vertical) {}
template <typename T>
Vector3Of<T>::Vector3Of(SphericalOf<T> v) {
float cosVertical = AngleOf<T>::Cos(v.direction.vertical);
float sinVertical = AngleOf<T>::Sin(v.direction.vertical);
float cosHorizontal = AngleOf<T>::Cos(v.direction.horizontal);
float sinHorizontal = AngleOf<T>::Sin(v.direction.horizontal);
horizontal = v.distance * sinVertical * sinHorizontal;
vertical = v.distance * cosVertical;
depth = v.distance * sinVertical * cosHorizontal;
}
template <typename T>
Vector3Of<T>::Vector3Of(Vector3 v) : horizontal(v.x), vertical(v.y), depth(v.z) {}
template <typename T>
Vector3 Vector3Of<T>::ToVector3() {
return Vector3(this->horizontal, this->vertical, this->depth);
}
template <typename T> template <typename T>
Vector3Of<T>::~Vector3Of() {} Vector3Of<T>::~Vector3Of() {}
template <>
Vector3Of<float> Vector3Of<float>::FromSpherical(SphericalOf<float> v) {
AngleOf<float> vertical = AngleOf<float>::Degrees(90) - v.direction.vertical;
AngleOf<float> horizontal = v.direction.horizontal;
float cosVertical = AngleOf<float>::Cos(vertical);
float sinVertical = AngleOf<float>::Sin(vertical);
float cosHorizontal = AngleOf<float>::Cos(horizontal);
float sinHorizontal = AngleOf<float>::Sin(horizontal);
Vector3Of<float> r = Vector3Of<float>();
r.horizontal = v.distance * sinVertical * sinHorizontal;
r.vertical = v.distance * cosVertical;
r.depth = v.distance * sinVertical * cosHorizontal;
return r;
}
template <typename T> template <typename T>
const Vector3Of<T> Vector3Of<T>::zero = Vector3Of(T{}, T{}, T{}); const Vector3Of<T> Vector3Of<T>::zero = Vector3Of(T{}, T{}, T{});
template <> template <>
const Vector3Of<int> Vector3Of<int>::unit = Vector3Of<int>(1, 1, 1); const Vector3Of<int> Vector3Of<int>::unit = Vector3Of<int>(1, 1, 1);
template <> template <>
const Vector3Of<float> Vector3Of<float>::unit = Vector3Of<float>(1.0f, 1.0f, 1.0f); const Vector3Of<float> Vector3Of<float>::unit =
Vector3Of<float>(1.0f, 1.0f, 1.0f);
template <> template <>
const Vector3Of<double> Vector3Of<double>::unit = Vector3Of<double>(1.0f, 1.0f, 1.0f); const Vector3Of<double> Vector3Of<double>::unit =
Vector3Of<double>(1.0f, 1.0f, 1.0f);
template <typename T>
const Vector3Of<T> Vector3Of<T>::forward = Vector3Of(T{}, T{}, 1);
// template <typename T> // template <typename T>
// const Vector3Of<T> Vector3Of<T>::unit = // const Vector3Of<T> Vector3Of<T>::unit =
// Vector3Of(TypeTraits<T>::unit(), TypeTraits<T>::unit(), TypeTraits<T>::unit()); // Vector3Of(TypeTraits<T>::unit(), TypeTraits<T>::unit(),
// TypeTraits<T>::unit());
// const Vector3Of Vector3Of::right = Vector3Of(1, 0, 0); // const Vector3Of Vector3Of::right = Vector3Of(1, 0, 0);
// const Vector3Of Vector3Of::left = Vector3Of(-1, 0, 0); // const Vector3Of Vector3Of::left = Vector3Of(-1, 0, 0);
// const Vector3Of Vector3Of::up = Vector3Of(0, 1, 0); // const Vector3Of Vector3Of::up = Vector3Of(0, 1, 0);
@ -278,18 +287,26 @@ const Vector3Of<double> Vector3Of<double>::unit = Vector3Of<double>(1.0f, 1.0f,
// const Vector3Of Vector3Of::back = Vector3Of(0, 0, -1); // const Vector3Of Vector3Of::back = Vector3Of(0, 0, -1);
template <typename T> template <typename T>
T Vector3Of<T>::MagnitudeOf(const Vector3Of& v) { bool Vector3Of<T>::operator==(const Vector3Of& v) const {
return sqrtf(v.horizontal * v.horizontal + v.vertical * v.vertical + v.depth * v.depth); return (this->horizontal == v.horizontal && this->vertical == v.vertical &&
this->depth == v.depth);
}
template <typename T>
float Vector3Of<T>::MagnitudeOf(const Vector3Of& v) {
return sqrtf(v.horizontal * v.horizontal + v.vertical * v.vertical +
v.depth * v.depth);
} }
template <typename T> template <typename T>
T Vector3Of<T>::Magnitude() const { float Vector3Of<T>::Magnitude() const {
return (T)sqrtf(this->horizontal * this->horizontal + this->vertical * this->vertical + return sqrtf(this->horizontal * this->horizontal +
this->depth * this->depth); this->vertical * this->vertical + this->depth * this->depth);
} }
template <typename T> template <typename T>
float Vector3Of<T>::SqrMagnitudeOf(const Vector3Of& v) { float Vector3Of<T>::SqrMagnitudeOf(const Vector3Of& v) {
return v.horizontal * v.horizontal + v.vertical * v.vertical + v.depth * v.depth; return v.horizontal * v.horizontal + v.vertical * v.vertical +
v.depth * v.depth;
} }
template <typename T> template <typename T>
float Vector3Of<T>::SqrMagnitude() const { float Vector3Of<T>::SqrMagnitude() const {
@ -316,9 +333,13 @@ Vector3Of<T> Vector3Of<T>::Normalized() const {
return result; return result;
} }
template <typename T> template <typename T>
Vector3Of<T> Vector3Of<T>::operator-() const {
return Vector3Of<T>(-this->horizontal, -this->vertical, -this->depth);
}
template <typename T>
Vector3Of<T> Vector3Of<T>::operator-(const Vector3Of& v) const { Vector3Of<T> Vector3Of<T>::operator-(const Vector3Of& v) const {
return Vector3(this->horizontal - v.horizontal, this->vertical - v.vertical, return Vector3Of<T>(this->horizontal - v.horizontal,
this->depth - v.depth); this->vertical - v.vertical, this->depth - v.depth);
} }
template <typename T> template <typename T>
@ -331,8 +352,8 @@ Vector3Of<T> Vector3Of<T>::operator-=(const Vector3Of& v) {
template <typename T> template <typename T>
Vector3Of<T> Vector3Of<T>::operator+(const Vector3Of& v) const { Vector3Of<T> Vector3Of<T>::operator+(const Vector3Of& v) const {
return Vector3(this->horizontal + v.horizontal, this->vertical + v.vertical, return Vector3Of<T>(this->horizontal + v.horizontal,
this->depth + v.depth); this->vertical + v.vertical, this->depth + v.depth);
} }
template <typename T> template <typename T>
@ -343,6 +364,12 @@ Vector3Of<T> Vector3Of<T>::operator+=(const Vector3Of& v) {
return *this; return *this;
} }
template <typename T>
Vector3Of<T> Vector3Of<T>::Scale(const Vector3Of& v1, const Vector3Of& v2) {
return Vector3Of<T>(v1.horizontal * v2.horizontal, v1.vertical * v2.vertical,
v1.depth * v2.depth);
}
template <typename T> template <typename T>
Vector3Of<T> Vector3Of<T>::operator*=(float f) { Vector3Of<T> Vector3Of<T>::operator*=(float f) {
this->horizontal *= f; this->horizontal *= f;
@ -360,12 +387,64 @@ Vector3Of<T> Vector3Of<T>::operator/=(float f) {
} }
template <typename T> template <typename T>
float Vector3Of<T>::Dot(const Vector3Of& v1, const Vector3Of& v2) { float Vector3Of<T>::Distance(const Vector3Of& v1, const Vector3Of& v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical + v1.depth * v2.depth; return MagnitudeOf(v1 - v2);
} }
template <typename T> template <typename T>
AngleOf<float> Vector3Of<T>::UnsignedAngle(const Vector3Of& v1, const Vector3Of& v2) { T Vector3Of<T>::Dot(const Vector3Of& v1, const Vector3Of& v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical +
v1.depth * v2.depth;
}
template <typename T>
Vector3Of<T> Vector3Of<T>::Cross(const Vector3Of& v1, const Vector3Of& v2) {
return Vector3Of<T>(
v1.vertical * v2.depth - v1.depth * v2.vertical,
v1.depth * v2.horizontal - v1.horizontal * v2.depth,
v1.horizontal * v2.vertical - v1.vertical * v2.horizontal);
}
template <typename T>
Vector3Of<float> Vector3Of<T>::Project(const Vector3Of& v, const Vector3Of& n) {
T sqrMagnitude = Dot(n, n);
if (sqrMagnitude < epsilon)
return Vector3Of<float>::zero;
else {
T dot = Dot(v, n);
Vector3Of<float> r = n * dot;
r /= sqrMagnitude;
return r;
}
}
template <typename T>
Vector3Of<float> Vector3Of<T>::ProjectOnPlane(const Vector3Of& v,
const Vector3Of& n) {
Vector3Of<float> r = (Vector3Of<float>)v - Project(v, n);
return r;
}
template <typename T>
AngleOf<float> Vector3Of<T>::SignedAngle(const Vector3Of& v1,
const Vector3Of& v2,
const Vector3Of& axis) {
// angle in [0,180]
AngleOf<float> angle = Vector3Of<T>::UnsignedAngle(v1, v2);
Vector3Of<T> cross = Vector3Of<T>::Cross(v1, v2);
float b = Vector3Of<T>::Dot(axis, cross);
float signd = b < 0 ? -1.0F : (b > 0 ? 1.0F : 0.0F);
// angle in [-179,180]
AngleOf<float> signed_angle = angle * signd;
return AngleOf<float>(signed_angle);
}
template <typename T>
AngleOf<float> Vector3Of<T>::UnsignedAngle(const Vector3Of& v1,
const Vector3Of& v2) {
float denominator = sqrtf(v1.SqrMagnitude() * v2.SqrMagnitude()); float denominator = sqrtf(v1.SqrMagnitude() * v2.SqrMagnitude());
if (denominator < epsilon) if (denominator < epsilon)
return AngleOf<float>(); return AngleOf<float>();
@ -373,14 +452,26 @@ AngleOf<float> Vector3Of<T>::UnsignedAngle(const Vector3Of& v1, const Vector3Of&
float dot = Vector3Of::Dot(v1, v2); float dot = Vector3Of::Dot(v1, v2);
float fraction = dot / denominator; float fraction = dot / denominator;
if (isnan(fraction)) if (isnan(fraction))
return AngleOf<float>::Degrees(fraction); // short cut to returning NaN universally return AngleOf<float>::Degrees(
fraction); // short cut to returning NaN universally
float cdot = clamp(fraction, -1.0, 1.0); float cdot = Float::Clamp(fraction, -1.0, 1.0);
float r = ((float)acos(cdot)); float r = ((float)acos(cdot));
return AngleOf<float>::Radians(r); return AngleOf<float>::Radians(r);
} }
template <typename T>
Vector3Of<float> Vector3Of<T>::Lerp(const Vector3Of& v1,
const Vector3Of& v2,
float f) {
Vector3Of<float> v1f = v1;
Vector3Of<float> delta = v2 - v1;
Vector3Of<float> r = v1f + delta * f;
return r;
}
template class Vector3Of<float>; template class Vector3Of<float>;
template class Vector3Of<int>;
} // namespace LinearAlgebra } // namespace LinearAlgebra

140
Vector3.h
View File

@ -7,7 +7,7 @@
#include "Vector2.h" #include "Vector2.h"
/*
extern "C" { extern "C" {
/// <summary> /// <summary>
/// 3-dimensional Vector representation (C-style) /// 3-dimensional Vector representation (C-style)
@ -31,12 +31,14 @@ typedef struct Vec3 {
} Vec3; } Vec3;
} }
*/
namespace LinearAlgebra { namespace LinearAlgebra {
template <typename T> template <typename T>
class SphericalOf; class SphericalOf;
/*
/// @brief A 3-dimensional vector /// @brief A 3-dimensional vector
/// @remark This uses a right-handed carthesian coordinate system. /// @remark This uses a right-handed carthesian coordinate system.
/// @note This implementation intentionally avoids the use of x, y and z values. /// @note This implementation intentionally avoids the use of x, y and z values.
@ -147,8 +149,8 @@ struct Vector3 : Vec3 {
/// @return The scaled vector /// @return The scaled vector
/// @remark Each component of the vector will be multipled with the same /// @remark Each component of the vector will be multipled with the same
/// factor f. /// factor f.
friend Vector3 operator*(const Vector3& v, float f) { return Vector3(v.x * f, v.y * f, v.z * f); } friend Vector3 operator*(const Vector3& v, float f) { return Vector3(v.x * f,
friend Vector3 operator*(float f, const Vector3& v) { v.y * f, v.z * f); } friend Vector3 operator*(float f, const Vector3& v) {
// return Vector3(f * v.x, f * v.y, f * v.z); // return Vector3(f * v.x, f * v.y, f * v.z);
return Vector3(v.x * f, v.y * f, v.z * f); return Vector3(v.x * f, v.y * f, v.z * f);
} }
@ -157,8 +159,8 @@ struct Vector3 : Vec3 {
/// @param f The scaling factor /// @param f The scaling factor
/// @return The scaled vector /// @return The scaled vector
/// @remark Each componet of the vector will be divided by the same factor. /// @remark Each componet of the vector will be divided by the same factor.
friend Vector3 operator/(const Vector3& v, float f) { return Vector3(v.x / f, v.y / f, v.z / f); } friend Vector3 operator/(const Vector3& v, float f) { return Vector3(v.x / f,
friend Vector3 operator/(float f, const Vector3& v) { v.y / f, v.z / f); } friend Vector3 operator/(float f, const Vector3& v) {
// return Vector3(f / v.x, f / v.y, f / v.z); // return Vector3(f / v.x, f / v.y, f / v.z);
return Vector3(v.x / f, v.y / f, v.z / f); return Vector3(v.x / f, v.y / f, v.z / f);
} }
@ -207,7 +209,8 @@ struct Vector3 : Vec3 {
/// @param v2 The ending vector /// @param v2 The ending vector
/// @param axis The axis to rotate around /// @param axis The axis to rotate around
/// @return The signed angle between the two vectors /// @return The signed angle between the two vectors
static AngleOf<float> SignedAngle(const Vector3& v1, const Vector3& v2, const Vector3& axis); static AngleOf<float> SignedAngle(const Vector3& v1, const Vector3& v2, const
Vector3& axis);
/// @brief Lerp (linear interpolation) between two vectors /// @brief Lerp (linear interpolation) between two vectors
/// @param v1 The starting vector /// @param v1 The starting vector
@ -219,57 +222,86 @@ struct Vector3 : Vec3 {
/// between *v1* and *v2* etc. /// between *v1* and *v2* etc.
static Vector3 Lerp(const Vector3& v1, const Vector3& v2, float f); static Vector3 Lerp(const Vector3& v1, const Vector3& v2, float f);
}; };
*/
/// @brief A 3-dimensional vector /// @brief A 3-dimensional vector
/// @remark This uses a right-handed carthesian coordinate system. /// @remark This uses a right-handed carthesian coordinate system.
/// @remark No default unit (for instance, meters) is specified /// @remark No default unit (for instance, meters) is specified
/// @note This implementation intentionally avoids the use of x, y and z values. /// @note This implementation intentionally avoids the use of x, y and z values.
/// @tparam T The type of the paramters used to measure distance. Typical values are float and int /// @tparam T The type of the paramters used to measure distance. Typical values
/// are float and int
template <typename T> template <typename T>
class Vector3Of { class Vector3Of {
public: public:
/// @brief The distance in the horizontal direction, left = negative, right = positive /// @brief The distance in the horizontal direction, left = negative, right =
/// positive
T horizontal = T{}; T horizontal = T{};
/// @brief The distance in the vertical direction, down = negative, up = positive /// @brief The distance in the vertical direction, down = negative, up =
/// positive
T vertical = T{}; T vertical = T{};
/// @brief The distance in the depth direction, backward = negative, forward = positive /// @brief The distance in the depth direction, backward = negative, forward =
/// positive
T depth = T{}; T depth = T{};
/// @brief A new 3-dimensional zero vector /// @brief A new 3-dimensional zero vector
Vector3Of(); Vector3Of();
/// @brief A new 3-dimensional vector /// @brief A new 3-dimensional vector
/// @param horizontal The distance in the horizontal direction, left = negative, right = /// @param horizontal The distance in the horizontal direction, left =
/// positive /// negative, right = positive
/// @param vertical The distance in the vertical direction, down = negative, up = /// @param vertical The distance in the vertical direction, down = negative,
/// positive /// up = positive
/// @param depth The distance in the depth direction, backward = negative, forward = /// @param depth The distance in the depth direction, backward = negative,
/// positive /// forward = positive
Vector3Of(T horizontal, T vertical, T depth); Vector3Of(T horizontal, T vertical, T depth);
Vector3Of(Vector2Of<T> v); Vector3Of(Vector2Of<T> v);
Vector3Of(SphericalOf<T> v); // Vector3Of(SphericalOf<T> v);
Vector3Of(Vector3 v);
Vector3 ToVector3();
/// @brief Vector3 destructor /// @brief Vector3 destructor
~Vector3Of(); ~Vector3Of();
/// @brief Converting constructor: allow Vector2Of<U> -> Vector2Of<T>
/// @tparam U
/// @param other
template <typename U>
constexpr Vector3Of(const Vector3Of<U>& other) noexcept
: horizontal(static_cast<T>(other.horizontal)),
vertical(static_cast<T>(other.vertical)),
depth(static_cast<T>(other.depth)) {}
static Vector3Of<float> FromSpherical(SphericalOf<float> v);
/// @brief A vector with zero for all axis /// @brief A vector with zero for all axis
const static Vector3Of zero; const static Vector3Of zero;
/// @brief A vector with unit for all axis /// @brief A vector with unit for all axis
const static Vector3Of unit; const static Vector3Of unit;
// const Vector3Of<int> Vector3Of<int>::unit(1, 1, 1); // Hardcoded unit vector for int /// @brief A normalized forward-oriented vector
// const Vector3Of<float> Vector3Of<float>::unit(1.0f, 1.0f, 1.0f); // Hardcoded unit vector for const static Vector3Of forward;
// float /// @brief A normalized back-oriented vector
const static Vector3Of back;
/// @brief A normalized right-oriented vector
const static Vector3Of right;
/// @brief A normalized left-oriented vector
const static Vector3Of left;
/// @brief A normalized up-oriented vector
const static Vector3Of up;
/// @brief A normalized down-oriented vector
const static Vector3Of down;
/// @brief Check if this vector to the given vector
/// @param v The vector to check against
/// @return true if it is identical to the given vector
/// @note This uses float comparison to check equality which may have strange
/// effects. Equality on floats should be avoided.
bool operator==(const Vector3Of& v) const;
/// @brief The vector length /// @brief The vector length
/// @param v The vector for which you need the length /// @param v The vector for which you need the length
/// @return The vector length /// @return The vector length
static T MagnitudeOf(const Vector3Of& v); static float MagnitudeOf(const Vector3Of& v);
/// @brief The vector length /// @brief The vector length
/// @return The vector length /// @return The vector length
T Magnitude() const; float Magnitude() const;
/// @brief The squared vector length /// @brief The squared vector length
/// @param v The vector for which you need the length /// @param v The vector for which you need the length
@ -293,6 +325,10 @@ class Vector3Of {
/// @return The vector normalized to a length of 1 /// @return The vector normalized to a length of 1
Vector3Of<T> Normalized() const; Vector3Of<T> Normalized() const;
/// @brief Negate te vector such that it points in the opposite direction
/// @return The negated vector
Vector3Of<T> operator-() const;
/// @brief Subtract a vector from this vector /// @brief Subtract a vector from this vector
/// @param v The vector to subtract from this vector /// @param v The vector to subtract from this vector
/// @return The result of this subtraction /// @return The result of this subtraction
@ -305,6 +341,14 @@ class Vector3Of {
Vector3Of<T> operator+(const Vector3Of& v) const; Vector3Of<T> operator+(const Vector3Of& v) const;
Vector3Of<T> operator+=(const Vector3Of& v); Vector3Of<T> operator+=(const Vector3Of& v);
/// @brief Scale the vector using another vector
/// @param v1 The vector to scale
/// @param v2 A vector with the scaling factors
/// @return The scaled vector
/// @remark Each component of the vector v1 will be multiplied with the
/// matching component from the scaling vector v2.
static Vector3Of<T> Scale(const Vector3Of& v1, const Vector3Of& v2);
/// @brief Scale the vector uniformly up /// @brief Scale the vector uniformly up
/// @param f The scaling factor /// @param f The scaling factor
/// @return The scaled vector /// @return The scaled vector
@ -332,11 +376,35 @@ class Vector3Of {
} }
Vector3Of<T> operator/=(float f); Vector3Of<T> operator/=(float f);
/// @brief The distance between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The distance between the two vectors
static float Distance(const Vector3Of& v1, const Vector3Of& v2);
/// @brief The dot product of two vectors /// @brief The dot product of two vectors
/// @param v1 The first vector /// @param v1 The first vector
/// @param v2 The second vector /// @param v2 The second vector
/// @return The dot product of the two vectors /// @return The dot product of the two vectors
static float Dot(const Vector3Of& v1, const Vector3Of& v2); static T Dot(const Vector3Of& v1, const Vector3Of& v2);
/// @brief The cross product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The cross product of the two vectors
static Vector3Of<T> Cross(const Vector3Of& v1, const Vector3Of& v2);
/// @brief Project the vector on another vector
/// @param v The vector to project
/// @param n The normal vecto to project on
/// @return The projected vector
static Vector3Of<float> Project(const Vector3Of& v, const Vector3Of& n);
/// @brief Project the vector on a plane defined by a normal orthogonal to the
/// plane.
/// @param v The vector to project
/// @param n The normal of the plane to project on
/// @return Teh projected vector
static Vector3Of<float> ProjectOnPlane(const Vector3Of& v, const Vector3Of& n);
/// @brief The angle between two vectors /// @brief The angle between two vectors
/// @param v1 The first vector /// @param v1 The first vector
@ -346,6 +414,26 @@ class Vector3Of {
/// between the two vectors. Use Vector3::SignedAngle if a signed angle is /// between the two vectors. Use Vector3::SignedAngle if a signed angle is
/// needed. /// needed.
static AngleOf<float> UnsignedAngle(const Vector3Of& v1, const Vector3Of& v2); static AngleOf<float> UnsignedAngle(const Vector3Of& v1, const Vector3Of& v2);
/// @brief The signed angle between two vectors
/// @param v1 The starting vector
/// @param v2 The ending vector
/// @param axis The axis to rotate around
/// @return The signed angle between the two vectors
static AngleOf<float> SignedAngle(const Vector3Of& v1,
const Vector3Of& v2,
const Vector3Of& axis);
/// @brief Lerp (linear interpolation) between two vectors
/// @param v1 The starting vector
/// @param v2 The ending vector
/// @param f The interpolation distance
/// @return The lerped vector
/// @remark The factor f is unclamped. Value 0 matches the vector *v1*, Value
/// 1 matches vector *v2*. Value -1 is vector *v1* minus the difference
/// between *v1* and *v2* etc.
static Vector3Of<float> Lerp(const Vector3Of& v1,
const Vector3Of& v2,
float f);
}; };
using Vector3Int = Vector3Of<int>; using Vector3Int = Vector3Of<int>;

View File

@ -10,7 +10,6 @@ using namespace LinearAlgebra;
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
//using AngleTypes = ::testing::Types<AngleOf<float>, AngleOf<signed short>, AngleOf<signed char>>;
using BaseTypes = ::testing::Types<float, signed short, signed char>; using BaseTypes = ::testing::Types<float, signed short, signed char>;
template <typename T> template <typename T>

View File

@ -1,128 +1,151 @@
#if GTEST #if GTEST
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <limits>
#include <math.h> #include <math.h>
#include <chrono> #include <chrono>
#include <limits>
#include "Polar.h" #include "Polar.h"
#include "Spherical.h" #include "Spherical.h"
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
using Vector2 = Vector2Of<float>; using BaseTypes = ::testing::Types<float, signed short>;
template <typename T>
class PolarTests : public ::testing::Test {};
TYPED_TEST_SUITE(PolarTests, BaseTypes);
TYPED_TEST(PolarTests, FromVector2) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
using Polar = PolarOf<float>;
TEST(Polar, FromVector2) {
Vector2 v = Vector2(0, 1); Vector2 v = Vector2(0, 1);
PolarSingle p = PolarSingle::FromVector2(v); Polar p = Polar::FromVector2(v);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance 0 1"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance 0 1";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "s.angle 0 0 1"; EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "s.angle 0 0 1";
v = Vector2(1, 0); v = Vector2(1, 0);
p = PolarSingle::FromVector2(v); p = Polar::FromVector2(v);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance 1 0"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance 1 0";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 90.0F) << "s.angle 1 0"; EXPECT_FLOAT_EQ(p.angle.InDegrees(), 90.0F) << "s.angle 1 0";
v = Vector2(-1, 1); v = Vector2(-1, 1);
p = PolarSingle::FromVector2(v); p = Polar::FromVector2(v);
EXPECT_FLOAT_EQ(p.distance, sqrt(2.0F)) << "p.distance -1 1"; EXPECT_FLOAT_EQ(p.distance, sqrt(2.0F)) << "p.distance -1 1";
EXPECT_NEAR(p.angle.InDegrees(), -45.0F, 1.0e-05) << "s.angle -1 1"; EXPECT_NEAR(p.angle.InDegrees(), -45.0F, 1.0e-05) << "s.angle -1 1";
} }
TEST(Polar, FromSpherical) { TYPED_TEST(PolarTests, FromSpherical) {
SphericalSingle s; using T = TypeParam;
PolarSingle p; using Spherical = SphericalOf<T>;
using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
s = SphericalSingle(1, DirectionSingle::forward); Spherical s;
p = PolarSingle::FromSpherical(s); Polar p;
s = Spherical(1, DirectionOf<T>::forward);
p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 0 0)"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 0 0)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(1 0 0)"; EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(1 0 0)";
s = SphericalSingle(1, AngleSingle::Degrees(45), AngleSingle::Degrees(0)); s = Spherical(1, AngleOf<T>::Degrees(45), AngleOf<T>::Degrees(0));
p = PolarSingle::FromSpherical(s); p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 45 0)"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 45 0)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 45.0F) EXPECT_FLOAT_EQ(p.angle.InDegrees(), 45.0F)
<< "p.angle FromSpherical(1 45 0)"; << "p.angle FromSpherical(1 45 0)";
s = SphericalSingle(1, AngleSingle::Degrees(-45), AngleSingle::Degrees(0)); s = Spherical(1, Angle::Degrees(-45), Angle::Degrees(0));
p = PolarSingle::FromSpherical(s); p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 -45 0)"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(1 -45 0)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), -45.0F) EXPECT_FLOAT_EQ(p.angle.InDegrees(), -45.0F)
<< "p.angle FromSpherical(1 -45 0)"; << "p.angle FromSpherical(1 -45 0)";
s = SphericalSingle(0, AngleSingle::Degrees(0), AngleSingle::Degrees(0)); s = Spherical(0, Angle::Degrees(0), Angle::Degrees(0));
p = PolarSingle::FromSpherical(s); p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 0.0F) << "p.distance FromSpherical(0 0 0)"; EXPECT_FLOAT_EQ(p.distance, 0.0F) << "p.distance FromSpherical(0 0 0)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(0 0 0)"; EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(0 0 0)";
s = SphericalSingle(-1, AngleSingle::Degrees(0), AngleSingle::Degrees(0)); s = Spherical(-1, Angle::Degrees(0), Angle::Degrees(0));
p = PolarSingle::FromSpherical(s); p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(-1 0 0)"; EXPECT_FLOAT_EQ(p.distance, 1.0F) << "p.distance FromSpherical(-1 0 0)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), -180.0F) EXPECT_FLOAT_EQ(p.angle.InDegrees(), -180.0F)
<< "p.angle FromSpherical(-1 0 0)"; << "p.angle FromSpherical(-1 0 0)";
s = SphericalSingle(0, AngleSingle::Degrees(0), AngleSingle::Degrees(90)); s = Spherical(0, Angle::Degrees(0), Angle::Degrees(90));
p = PolarSingle::FromSpherical(s); p = Polar::FromSpherical(s);
EXPECT_FLOAT_EQ(p.distance, 0.0F) << "p.distance FromSpherical(0 0 90)"; EXPECT_FLOAT_EQ(p.distance, 0.0F) << "p.distance FromSpherical(0 0 90)";
EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(0 0 90)"; EXPECT_FLOAT_EQ(p.angle.InDegrees(), 0.0F) << "p.angle FromSpherical(0 0 90)";
} }
TEST(Polar, Negation) { TYPED_TEST(PolarTests, Negation) {
PolarSingle v = PolarSingle(2, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle r = PolarSingle::zero; using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
Polar v = Polar(2, Angle::Degrees(45));
Polar r = Polar::zero;
r = -v; r = -v;
EXPECT_FLOAT_EQ(r.distance, 2); EXPECT_FLOAT_EQ(r.distance, 2);
EXPECT_FLOAT_EQ(r.angle.InDegrees(), -135); EXPECT_FLOAT_EQ(r.angle.InDegrees(), -135);
EXPECT_TRUE(r == PolarSingle(2, AngleSingle::Degrees(-135))) EXPECT_TRUE(r == Polar(2, Angle::Degrees(-135))) << "Negate(2 45)";
<< "Negate(2 45)";
v = PolarSingle::Deg(2, -45); v = Polar::Deg(2, -45);
r = -v; r = -v;
EXPECT_TRUE(r == PolarSingle(2, AngleSingle::Degrees(135))) EXPECT_TRUE(r == Polar(2, Angle::Degrees(135))) << "Negate(2 -45)";
<< "Negate(2 -45)";
v = PolarSingle::Degrees(2, 0); v = Polar::Degrees(2, 0);
r = -v; r = -v;
EXPECT_TRUE(r == PolarSingle(2, AngleSingle::Degrees(180))) << "Negate(2 0)"; EXPECT_TRUE(r == Polar(2, Angle::Degrees(180))) << "Negate(2 0)";
v = PolarSingle(0, AngleSingle::Degrees(0)); v = Polar(0, Angle::Degrees(0));
r = -v; r = -v;
EXPECT_FLOAT_EQ(r.distance, 0.0f); EXPECT_FLOAT_EQ(r.distance, 0.0f);
EXPECT_FLOAT_EQ(r.angle.InDegrees(), 0.0f); EXPECT_FLOAT_EQ(r.angle.InDegrees(), 0.0f);
EXPECT_TRUE(r == PolarSingle(0, AngleSingle::Degrees(0))) << "Negate(0 0)"; EXPECT_TRUE(r == Polar(0, Angle::Degrees(0))) << "Negate(0 0)";
} }
TEST(Polar, Subtraction) { TYPED_TEST(PolarTests, Subtraction) {
PolarSingle v1 = PolarSingle(4, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle v2 = PolarSingle(1, AngleSingle::Degrees(-90)); using Polar = PolarOf<T>;
PolarSingle r = PolarSingle::zero; using Angle = AngleOf<T>;
Polar v1 = Polar(4, Angle::Degrees(45));
Polar v2 = Polar(1, Angle::Degrees(-90));
Polar r = Polar::zero;
r = v1 - v2; r = v1 - v2;
// don't know what to expect yet // don't know what to expect yet
v2 = PolarSingle::zero; v2 = Polar::zero;
r = v1 - v2; r = v1 - v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Subtraction(0 0)"; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Subtraction(0 0)";
} }
TEST(Polar, Addition) { TYPED_TEST(PolarTests, Addition) {
PolarSingle v1 = PolarSingle(1, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle v2 = PolarSingle(1, AngleSingle::Degrees(-90)); using Polar = PolarOf<T>;
PolarSingle r = PolarSingle::zero; using Angle = AngleOf<T>;
Polar v1 = Polar(1, Angle::Degrees(45));
Polar v2 = Polar(1, Angle::Degrees(-90));
Polar r = Polar::zero;
r = v1 - v2; r = v1 - v2;
// don't know what to expect yet // don't know what to expect yet
v2 = PolarSingle::zero; v2 = Polar::zero;
r = v1 + v2; r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0)"; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0)";
@ -130,15 +153,19 @@ TEST(Polar, Addition) {
r += v2; r += v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0)"; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0)";
v2 = PolarSingle(1, AngleSingle::Degrees(-45)); v2 = Polar(1, Angle::Degrees(-45));
r = v1 + v2; r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(0 0 0)"; EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(0 0 0)";
EXPECT_FLOAT_EQ(r.angle.InDegrees(), 0) << "Addition(0 0 0)"; EXPECT_FLOAT_EQ(r.angle.InDegrees(), 0) << "Addition(0 0 0)";
} }
TEST(Polar, Scale_Multiply) { TYPED_TEST(PolarTests, Scale_Multiply) {
PolarSingle v1 = PolarSingle(4, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle r = PolarSingle::zero; using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
Polar v1 = Polar(4, Angle::Degrees(45));
Polar r = Polar::zero;
r = v1 * 2.0f; r = v1 * 2.0f;
EXPECT_FLOAT_EQ(r.distance, v1.distance * 2) << "ScaleMult(4 45, 2)"; EXPECT_FLOAT_EQ(r.distance, v1.distance * 2) << "ScaleMult(4 45, 2)";
@ -146,9 +173,13 @@ TEST(Polar, Scale_Multiply) {
<< "ScaleMult(4 45, 2)"; << "ScaleMult(4 45, 2)";
} }
TEST(Polar, Scale_Divide) { TYPED_TEST(PolarTests, Scale_Divide) {
PolarSingle v1 = PolarSingle(4, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle r = PolarSingle::zero; using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
Polar v1 = Polar(4, Angle::Degrees(45));
Polar r = Polar::zero;
r = v1 / 2.0f; r = v1 / 2.0f;
EXPECT_FLOAT_EQ(r.distance, v1.distance / 2) << "ScaleDiv(4 45, 2)"; EXPECT_FLOAT_EQ(r.distance, v1.distance / 2) << "ScaleDiv(4 45, 2)";
@ -156,30 +187,38 @@ TEST(Polar, Scale_Divide) {
<< "ScaleDiv(4 45, 2)"; << "ScaleDiv(4 45, 2)";
} }
TEST(Polar, Distance) { TYPED_TEST(PolarTests, Distance) {
PolarSingle v1 = PolarSingle(4, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle v2 = PolarSingle(1, AngleSingle::Degrees(-90)); using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
Polar v1 = Polar(4, Angle::Degrees(45));
Polar v2 = Polar(1, Angle::Degrees(-90));
float d = 0; float d = 0;
d = PolarSingle::Distance(v1, v2); d = Polar::Distance(v1, v2);
// don't know what to expect yet // don't know what to expect yet
v2 = PolarSingle::zero; v2 = Polar::zero;
d = PolarSingle::Distance(v1, v2); d = Polar::Distance(v1, v2);
EXPECT_FLOAT_EQ(d, v1.distance) << "Distance(4 45, zero)"; EXPECT_FLOAT_EQ(d, v1.distance) << "Distance(4 45, zero)";
} }
TEST(Polar, Rotate) { TYPED_TEST(PolarTests, Rotate) {
PolarSingle v = PolarSingle(4, AngleSingle::Degrees(45)); using T = TypeParam;
PolarSingle r = PolarSingle::zero; using Polar = PolarOf<T>;
using Angle = AngleOf<T>;
r = PolarSingle::Rotate(v, AngleSingle::Degrees(45)); Polar v = Polar(4, Angle::Degrees(45));
Polar r = Polar::zero;
r = Polar::Rotate(v, Angle::Degrees(45));
EXPECT_FLOAT_EQ(r.distance, v.distance) << "Rotate(4 45, 45)"; EXPECT_FLOAT_EQ(r.distance, v.distance) << "Rotate(4 45, 45)";
EXPECT_FLOAT_EQ(r.angle.InDegrees(), 90.0f) << "Rotate(4 45, 45)"; EXPECT_FLOAT_EQ(r.angle.InDegrees(), 90.0f) << "Rotate(4 45, 45)";
} }
// Performance Test // Performance Test
TEST(PolarOfTest, PerformanceTest) { TYPED_TEST(PolarTests, PerformanceTest) {
const int numIterations = 1000000; // Number of instances to test const int numIterations = 1000000; // Number of instances to test
std::vector<PolarOf<float>> polarObjects; std::vector<PolarOf<float>> polarObjects;
@ -189,8 +228,8 @@ TEST(PolarOfTest, PerformanceTest) {
for (int i = 0; i < numIterations; ++i) { for (int i = 0; i < numIterations; ++i) {
float distance = float distance =
static_cast<float>(rand() % 100); // Random distance from 0 to 100 static_cast<float>(rand() % 100); // Random distance from 0 to 100
AngleOf<float> angle = AngleOf<float>::Degrees( AngleOf<float> angle = AngleOf<float>::Degrees(static_cast<float>(
static_cast<float>(rand() % 360)); // Random angle from 0 to 360 degrees rand() % 360)); // Random angle from 0 to 360 degrees
PolarOf<float> p = PolarOf<float>(distance, angle); PolarOf<float> p = PolarOf<float>(distance, angle);
polarObjects.emplace_back(p); // Create and store the object polarObjects.emplace_back(p); // Create and store the object
} }
@ -210,14 +249,15 @@ TEST(PolarOfTest, PerformanceTest) {
} }
// Edge Case 1: Testing with distance = 0 and angle = 45 // Edge Case 1: Testing with distance = 0 and angle = 45
TEST(PolarOfTest, TestDistanceZero) { TYPED_TEST(PolarTests, TestDistanceZero) {
PolarOf<float> p1(0.0f, AngleOf<float>::Degrees(45.0f)); PolarOf<float> p1(0.0f, AngleOf<float>::Degrees(45.0f));
EXPECT_EQ(p1.distance, 0.0f); // Ensure distance is 0 EXPECT_EQ(p1.distance, 0.0f); // Ensure distance is 0
EXPECT_EQ(p1.angle.InDegrees(), 0.0f); // Ensure angle is 0 when distance is 0 EXPECT_EQ(p1.angle.InDegrees(),
0.0f); // Ensure angle is 0 when distance is 0
} }
// Edge Case 2: Testing with negative distance, angle should be adjusted // Edge Case 2: Testing with negative distance, angle should be adjusted
TEST(PolarOfTest, TestNegativeDistance) { TYPED_TEST(PolarTests, TestNegativeDistance) {
PolarOf<float> p2(-10.0f, AngleOf<float>::Degrees(90.0f)); PolarOf<float> p2(-10.0f, AngleOf<float>::Degrees(90.0f));
EXPECT_EQ(p2.distance, 10.0f); // Ensure distance is positive EXPECT_EQ(p2.distance, 10.0f); // Ensure distance is positive
EXPECT_NEAR(p2.angle.InDegrees(), -90.0f, EXPECT_NEAR(p2.angle.InDegrees(), -90.0f,
@ -225,7 +265,7 @@ TEST(PolarOfTest, TestNegativeDistance) {
} }
// Edge Case 3: Testing with positive distance and angle = 180 // Edge Case 3: Testing with positive distance and angle = 180
TEST(PolarOfTest, TestPositiveDistance) { TYPED_TEST(PolarTests, TestPositiveDistance) {
PolarOf<float> p3(100.0f, AngleOf<float>::Degrees(180.0f)); PolarOf<float> p3(100.0f, AngleOf<float>::Degrees(180.0f));
EXPECT_EQ(p3.distance, 100.0f); // Ensure distance is correct EXPECT_EQ(p3.distance, 100.0f); // Ensure distance is correct
EXPECT_NEAR(p3.angle.InDegrees(), -180.0f, EXPECT_NEAR(p3.angle.InDegrees(), -180.0f,

View File

@ -27,15 +27,15 @@ TEST(Quaternion, ToAngles) {
bool r = false; bool r = false;
Quaternion q1 = Quaternion(0, 0, 0, 1); Quaternion q1 = Quaternion(0, 0, 0, 1);
Vector3 v = Vector3::zero; Vector3Of<float> v = Vector3Of<float>::zero;
v = Quaternion::ToAngles(q1); v = Quaternion::ToAngles(q1);
r = v == Vector3(0, 0, 0); r = v == Vector3Of<float>(0, 0, 0);
EXPECT_TRUE(r) << "Quaternion::ToAngles 0 0 0 1"; EXPECT_TRUE(r) << "Quaternion::ToAngles 0 0 0 1";
q1 = Quaternion(1, 0, 0, 0); q1 = Quaternion(1, 0, 0, 0);
v = Quaternion::ToAngles(q1); v = Quaternion::ToAngles(q1);
r = v == Vector3(180, 0, 0); r = v == Vector3Of<float>(180, 0, 0);
// EXPECT_TRUE(r) << "Quaternion::ToAngles 1 0 0 0"; // EXPECT_TRUE(r) << "Quaternion::ToAngles 1 0 0 0";
// fails on MacOS? // fails on MacOS?
} }
@ -56,16 +56,16 @@ TEST(Quaternion, MultiplicationVector) {
bool r = false; bool r = false;
Quaternion q1 = Quaternion(0, 0, 0, 1); Quaternion q1 = Quaternion(0, 0, 0, 1);
Vector3 v1 = Vector3(0, 1, 0); Vector3Of<float> v1 = Vector3Of<float>(0, 1, 0);
Vector3 v = Vector3::zero; Vector3Of<float> v = Vector3Of<float>::zero;
v = q1 * v1; v = q1 * v1;
r = v == Vector3(0, 1, 0); r = v == Vector3Of<float>(0, 1, 0);
EXPECT_TRUE(r) << "0 0 0 1 * Vector 0 1 0"; EXPECT_TRUE(r) << "0 0 0 1 * Vector 0 1 0";
q1 = Quaternion(1, 0, 0, 0); q1 = Quaternion(1, 0, 0, 0);
v = q1 * v1; v = q1 * v1;
r = v == Vector3(0, -1, 0); r = v == Vector3Of<float>(0, -1, 0);
EXPECT_TRUE(r) << "1 0 0 0 * Vector 0 1 0"; EXPECT_TRUE(r) << "1 0 0 0 * Vector 0 1 0";
} }
@ -118,7 +118,7 @@ TEST(Quaternion, SlerpUnclamped) {
TEST(Quaternion, Euler) { TEST(Quaternion, Euler) {
bool r = false; bool r = false;
Vector3 v1 = Vector3(0, 0, 0); Vector3Of<float> v1 = Vector3Of<float>(0, 0, 0);
Quaternion q = Quaternion::identity; Quaternion q = Quaternion::identity;
q = Quaternion::Euler(v1); q = Quaternion::Euler(v1);
@ -129,7 +129,7 @@ TEST(Quaternion, Euler) {
r = q == Quaternion::identity; r = q == Quaternion::identity;
EXPECT_TRUE(r) << "Euler 0 0 0"; EXPECT_TRUE(r) << "Euler 0 0 0";
v1 = Vector3(90, 90, -90); v1 = Vector3Of<float>(90, 90, -90);
q = Quaternion::Euler(v1); q = Quaternion::Euler(v1);
r = q == Quaternion(0, 0.707106709F, -0.707106709F, 0); r = q == Quaternion(0, 0.707106709F, -0.707106709F, 0);
EXPECT_TRUE(r) << "Euler Vector 90 90 -90"; EXPECT_TRUE(r) << "Euler Vector 90 90 -90";
@ -142,7 +142,7 @@ TEST(Quaternion, Euler) {
TEST(Quaternion, GetAngleAround) { TEST(Quaternion, GetAngleAround) {
bool r = false; bool r = false;
Vector3 v1 = Vector3(0, 1, 0); Vector3Of<float> v1 = Vector3Of<float>(0, 1, 0);
Quaternion q1 = Quaternion(0, 0, 0, 1); Quaternion q1 = Quaternion(0, 0, 0, 1);
float f; float f;
@ -153,7 +153,7 @@ TEST(Quaternion, GetAngleAround) {
f = Quaternion::GetAngleAround(v1, q1); f = Quaternion::GetAngleAround(v1, q1);
EXPECT_FLOAT_EQ(f, 180) << "GetAngleAround 0 1 0 , 0 0.7 -0.7 0"; EXPECT_FLOAT_EQ(f, 180) << "GetAngleAround 0 1 0 , 0 0.7 -0.7 0";
v1 = Vector3(0, 0, 0); v1 = Vector3Of<float>(0, 0, 0);
f = Quaternion::GetAngleAround(v1, q1); f = Quaternion::GetAngleAround(v1, q1);
r = isnan(f); r = isnan(f);
EXPECT_TRUE(r) << "GetAngleAround 0 0 0 , 0 0.7 -0.7 0"; EXPECT_TRUE(r) << "GetAngleAround 0 0 0 , 0 0.7 -0.7 0";
@ -162,7 +162,7 @@ TEST(Quaternion, GetAngleAround) {
TEST(Quaternion, GetRotationAround) { TEST(Quaternion, GetRotationAround) {
bool r = false; bool r = false;
Vector3 v1 = Vector3(0, 1, 0); Vector3Of<float> v1 = Vector3Of<float>(0, 1, 0);
Quaternion q1 = Quaternion(0, 0, 0, 1); Quaternion q1 = Quaternion(0, 0, 0, 1);
Quaternion q = Quaternion::identity; Quaternion q = Quaternion::identity;
@ -175,7 +175,7 @@ TEST(Quaternion, GetRotationAround) {
r = q == Quaternion(0, 1, 0, 0); r = q == Quaternion(0, 1, 0, 0);
EXPECT_TRUE(r) << "GetRotationAround 0 1 0 , 0 0.7 -0.7 0"; EXPECT_TRUE(r) << "GetRotationAround 0 1 0 , 0 0.7 -0.7 0";
v1 = Vector3(0, 0, 0); v1 = Vector3Of<float>(0, 0, 0);
q = Quaternion::GetRotationAround(v1, q1); q = Quaternion::GetRotationAround(v1, q1);
r = isnan(q.x) && isnan(q.y) && isnan(q.z) && isnan(q.w); r = isnan(q.x) && isnan(q.y) && isnan(q.z) && isnan(q.w);
EXPECT_TRUE(r) << "GetRotationAround 0 0 0 , 0 0.7 -0.7 0"; EXPECT_TRUE(r) << "GetRotationAround 0 0 0 , 0 0.7 -0.7 0";

View File

@ -1,214 +0,0 @@
#if GTEST
#include <gtest/gtest.h>
#include <limits>
#include <math.h>
#include <chrono>
#include "Spherical.h"
#define FLOAT_INFINITY std::numeric_limits<float>::infinity()
TEST(SphericalSingle, FromVector3) {
Vector3 v = Vector3(0, 0, 1);
SphericalSingle s = SphericalSingle ::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 0 1";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 0 1";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 0 0 1";
v = Vector3(0, 1, 0);
s = SphericalSingle ::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 1 0";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 1 0";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 90.0F) << "s.vert 0 1 0";
v = Vector3(1, 0, 0);
s = SphericalSingle ::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 1 0 0";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 90.0F) << "s.hor 1 0 0";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 1 0 0";
}
TEST(SphericalSingle, FromPolar) {
PolarSingle p = PolarSingle(1, AngleSingle::Degrees(0));
SphericalSingle s = SphericalSingle ::FromPolar(p);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 0)";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F)
<< "s.hor Polar(1 0)";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert Polar(1 0)";
p = PolarSingle(1, AngleSingle::Degrees(45));
s = SphericalSingle ::FromPolar(p);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 45)";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 45.0F)
<< "s.hor Polar(1 45)";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert Polar(1 45)";
p = PolarSingle(1, AngleSingle::Degrees(-45));
s = SphericalSingle ::FromPolar(p);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 -45)";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), -45.0F)
<< "s.hor Polar(1 -45)";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert Polar(1 -45)";
p = PolarSingle(0, AngleSingle::Degrees(0));
s = SphericalSingle ::FromPolar(p);
EXPECT_FLOAT_EQ(s.distance, 0.0F) << "s.distance Polar(0 0)";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F)
<< "s.hor Polar(0 0)";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert Polar(0 0)";
p = PolarSingle(-1, AngleSingle::Degrees(0));
s = SphericalSingle ::FromPolar(p);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(-1 0)";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), -180.0F)
<< "s.hor Polar(-1 0)";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert Polar(-1 0)";
}
TEST(SphericalSingle, Incident1) {
Vector3 v = Vector3(2.242557f, 1.027884f, -0.322347f);
SphericalSingle s = SphericalSingle ::FromVector3(v);
SphericalSingle sr = SphericalSingle(2.49F, AngleSingle::Degrees(98.18f),
AngleSingle::Degrees(24.4F));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-01);
EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-02);
EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-02);
Vector3 r = Vector3(sr);
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-02) << "toVector3.x 1 0 0";
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-02) << "toVector3.y 1 0 0";
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-02) << "toVector3.z 1 0 0";
}
TEST(SphericalSingle, Incident2) {
Vector3 v = Vector3(1.0f, 0.0f, 1.0f);
SphericalSingle s = SphericalSingle ::FromVector3(v);
SphericalSingle sr = SphericalSingle(
1.4142135623F, AngleSingle::Degrees(45.0f), AngleSingle::Degrees(0.0F));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-05);
EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-05);
EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-05);
Vector3 r = Vector3(sr);
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-06);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06);
v = Vector3(0.0f, 1.0f, 1.0f);
s = SphericalSingle ::FromVector3(v);
sr = SphericalSingle(1.4142135623F, AngleSingle::Degrees(0.0f),
AngleSingle::Degrees(45.0F));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-05);
EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-05);
EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-05);
r = Vector3(sr);
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-06);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06);
v = Vector3(1.0f, 1.0f, 1.0f);
s = SphericalSingle ::FromVector3(v);
r = Vector3(s);
EXPECT_NEAR(s.distance, 1.73205080F, 1.0e-02);
EXPECT_NEAR(s.direction.horizontal.InDegrees(), 45.0F, 1.0e-02);
EXPECT_NEAR(s.direction.vertical.InDegrees(), 35.26F, 1.0e-02);
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-06);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06);
// s = SphericalSingle(10, 45, 45);
// r = s.ToVector3();
// EXPECT_NEAR(r.x, 5, 1.0e-06);
// EXPECT_NEAR(r.y, 7.07, 1.0e-06);
// EXPECT_NEAR(r.z, 5, 1.0e-06);
}
TEST(SphericalSingle, Addition) {
SphericalSingle v1 =
SphericalSingle(1, AngleSingle::Degrees(45), AngleSingle::Degrees(0));
SphericalSingle v2 = SphericalSingle ::zero;
SphericalSingle r = SphericalSingle ::zero;
r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)";
r = v1;
r += v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)";
v2 = SphericalSingle(1, AngleSingle::Degrees(-45), AngleSingle::Degrees(0));
r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 -45 0)";
EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 0) << "Addition(1 -45 0)";
EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 0) << "Addition(1 -45 0)";
v2 = SphericalSingle(1, AngleSingle::Degrees(0), AngleSingle::Degrees(90));
r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 0 90)";
EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 45) << "Addition(1 0 90)";
EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 45) << "Addition(1 0 90)";
}
TEST(SphericalSingle, AdditionPerformance) {
const int numIterations = 1000000; // Number of additions to test
std::vector<SphericalSingle> sphericalObjects;
// Populate the vector with random SphericalOf objects
for (int i = 0; i < numIterations; ++i) {
float distance = (float)(rand() % 100);
float horizontal = (float)(rand() % 180);
float vertical = (float)(rand() % 360);
SphericalSingle s = SphericalSingle::Deg(distance, horizontal, vertical);
sphericalObjects.push_back(s);
}
// Measure the time to perform multiple additions
auto start = std::chrono::high_resolution_clock::now();
SphericalSingle result = SphericalSingle::zero; // Start with a
// zero-initialized object
for (int i = 0; i < numIterations - 1; ++i) {
result = result + sphericalObjects[i]; // Add objects
// together
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = end - start;
std::cout << "Time to perform " << numIterations - 1
<< " additions: " << duration.count() << " seconds." << std::endl;
// Assert that the time taken is less than
// 1 second (or any other performance
// requirement)
ASSERT_LE(duration.count(), 1.0) << "Performance test failed: "
"Additions took longer than 1 "
"second.";
}
#endif

View File

@ -1,17 +1,26 @@
#if GTEST #if GTEST
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <limits>
#include <math.h> #include <math.h>
#include <chrono> #include <chrono>
#include <limits>
#include "Spherical.h" #include "Spherical.h"
#include "Vector3.h" #include "Vector3.h"
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
TEST(Spherical16, FromVector3) { using BaseTypes = ::testing::Types<float, signed short>;
Vector3 v = Vector3(0, 0, 1);
Spherical16 s = Spherical16::FromVector3(v); template <typename T>
class SphericalTests : public ::testing::Test {};
TYPED_TEST_SUITE(SphericalTests, BaseTypes);
TYPED_TEST(SphericalTests, FromVector3) {
using T = TypeParam;
using Spherical = SphericalOf<T>;
Vector3Of<float> v = Vector3Of<float>(0, 0, 1);
Spherical s = Spherical::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 0 1"; EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 0 1";
EXPECT_FLOAT_EQ((float)s.direction.horizontal.InDegrees(), 0.0F) EXPECT_FLOAT_EQ((float)s.direction.horizontal.InDegrees(), 0.0F)
@ -19,149 +28,167 @@ TEST(Spherical16, FromVector3) {
EXPECT_FLOAT_EQ((float)s.direction.vertical.InDegrees(), 0.0F) EXPECT_FLOAT_EQ((float)s.direction.vertical.InDegrees(), 0.0F)
<< "s.vert 0 0 1"; << "s.vert 0 0 1";
v = Vector3(0, 1, 0); v = Vector3Of<float>(0, 1, 0);
s = Spherical16::FromVector3(v); s = Spherical::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 1 0"; EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 1 0";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 1 0"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 1 0";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 90.0F) << "s.vert 0 1 0"; EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 90.0F) << "s.vert 0 1 0";
v = Vector3(1, 0, 0); v = Vector3Of<float>(1, 0, 0);
s = Spherical16::FromVector3(v); s = Spherical::FromVector3(v);
EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 1 0 0"; EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 1 0 0";
EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 90.0F) << "s.hor 1 0 0"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 90.0F) << "s.hor 1 0 0";
EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 1 0 0"; EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 1 0 0";
} }
TEST(Spherical16, Vector3) { TYPED_TEST(SphericalTests, Vector3) {
Vector3 v = Vector3(1, 2, 3); using T = TypeParam;
Spherical16 rd = Spherical16::FromVector3(v); using Spherical = SphericalOf<T>;
Vector3 rv = rd.ToVector3();
EXPECT_LT(Vector3::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical";
v = Vector3(1, 2, -3); Vector3Of<float> v = Vector3Of<float>(1, 2, 3);
rd = Spherical16::FromVector3(v); Spherical rd = Spherical::FromVector3(v);
Vector3Of<float> rv = rd.ToVector3();
EXPECT_LT(Vector3Of<float>::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical";
v = Vector3Of<float>(1, 2, -3);
rd = Spherical::FromVector3(v);
rv = rd.ToVector3(); rv = rd.ToVector3();
EXPECT_LT(Vector3::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical"; EXPECT_LT(Vector3Of<float>::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical";
} }
// TEST(Spherical16, FromPolar) { // TYPED_TEST(SphericalTests, FromPolar) {
// using T = TypeParam;
// using Spherical = SphericalOf<T>;
// Polar p = Polar(1, 0); // Polar p = Polar(1, 0);
// Spherical16 s = Spherical16::FromPolar(p); // Spherical s = Spherical::FromPolar(p);
// EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 0)"; // EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 0)";
// EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 0.0F) << "s.hor Polar(1 0)"; // EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 0.0F) << "s.hor Polar(1 0)";
// EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 0)"; // EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 0)";
// p = Polar(1, 45); // p = Polar(1, 45);
// s = Spherical16::FromPolar(p); // s = Spherical::FromPolar(p);
// EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 45)"; // EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 45)";
// EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 45.0F) << "s.hor Polar(1 45)"; // EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 45.0F) << "s.hor Polar(1 45)";
// EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 45)"; // EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 45)";
// p = Polar(1, -45); // p = Polar(1, -45);
// s = Spherical16::FromPolar(p); // s = Spherical::FromPolar(p);
// EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 -45)"; // EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 -45)";
// EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), -45.0F) << "s.hor Polar(1 -45)"; // EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), -45.0F) << "s.hor Polar(1 -45)";
// EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 -45)"; // EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(1 -45)";
// p = Polar(0, 0); // p = Polar(0, 0);
// s = Spherical16::FromPolar(p); // s = Spherical::FromPolar(p);
// EXPECT_FLOAT_EQ(s.distance, 0.0F) << "s.distance Polar(0 0)"; // EXPECT_FLOAT_EQ(s.distance, 0.0F) << "s.distance Polar(0 0)";
// EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 0.0F) << "s.hor Polar(0 0)"; // EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), 0.0F) << "s.hor Polar(0 0)";
// EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(0 0)"; // EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(0 0)";
// p = Polar(-1, 0); // p = Polar(-1, 0);
// s = Spherical16::FromPolar(p); // s = Spherical::FromPolar(p);
// EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(-1 0)"; // EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(-1 0)";
// EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), -180.0F) << "s.hor Polar(-1 0)"; // EXPECT_FLOAT_EQ(s.horizontal.InDegrees(), -180.0F) << "s.hor Polar(-1 0)";
// EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(-1 0)"; // EXPECT_FLOAT_EQ(s.vertical.InDegrees(), 0.0F) << "s.vert Polar(-1 0)";
// } // }
TEST(Spherical16, Incident1) { TYPED_TEST(SphericalTests, Incident1) {
Vector3 v = Vector3(2.242557f, 1.027884f, -0.322347f); using T = TypeParam;
Spherical16 s = Spherical16::FromVector3(v); using Spherical = SphericalOf<T>;
using Angle = AngleOf<T>;
Spherical16 sr = Vector3Of<float> v = Vector3Of<float>(2.242557f, 1.027884f, -0.322347f);
Spherical16(2.49F, Angle16::Degrees(98.18f), Angle16::Degrees(24.4F)); Spherical s = Spherical::FromVector3(v);
Spherical sr =
Spherical(2.49F, Angle::Degrees(98.18f), Angle::Degrees(24.4F));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-01); EXPECT_NEAR(s.distance, sr.distance, 1.0e-01);
EXPECT_NEAR(s.direction.horizontal.InDegrees(), EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-02); sr.direction.horizontal.InDegrees(), 1.0e-02);
EXPECT_NEAR(s.direction.vertical.InDegrees(), EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-02); sr.direction.vertical.InDegrees(), 1.0e-02);
Vector3 r = Vector3Of<float> r =
Spherical16(sr.distance, sr.direction.horizontal, sr.direction.vertical) Spherical(sr.distance, sr.direction.horizontal, sr.direction.vertical)
.ToVector3(); .ToVector3();
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-02) << "toVector3.x 1 0 0"; EXPECT_NEAR(r.horizontal, v.horizontal, 1.0e-02) << "toVector3.x 1 0 0";
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-02) << "toVector3.y 1 0 0"; EXPECT_NEAR(r.vertical, v.vertical, 1.0e-02) << "toVector3.y 1 0 0";
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-02) << "toVector3.z 1 0 0"; EXPECT_NEAR(r.depth, v.depth, 1.0e-02) << "toVector3.z 1 0 0";
} }
TEST(Spherical16, Incident2) { TYPED_TEST(SphericalTests, Incident2) {
Vector3 v = Vector3(1.0f, 0.0f, 1.0f); using T = TypeParam;
Spherical16 s = Spherical16::FromVector3(v); using Spherical = SphericalOf<T>;
using Angle = AngleOf<T>;
Spherical16 sr = Spherical16(1.4142135623F, Angle16::Degrees(45.0f), Vector3Of<float> v = Vector3Of<float>(1.0f, 0.0f, 1.0f);
Angle16::Degrees(0.0F)); Spherical s = Spherical::FromVector3(v);
Spherical sr =
Spherical(1.4142135623F, Angle::Degrees(45.0f), Angle::Degrees(0.0F));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-05); EXPECT_NEAR(s.distance, sr.distance, 1.0e-05);
EXPECT_NEAR(s.direction.horizontal.InDegrees(), EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-05); sr.direction.horizontal.InDegrees(), 1.0e-05);
EXPECT_NEAR(s.direction.vertical.InDegrees(), EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-05); sr.direction.vertical.InDegrees(), 1.0e-05);
Vector3 r = Vector3Of<float> r =
Spherical16(sr.distance, sr.direction.horizontal, sr.direction.vertical) Spherical(sr.distance, sr.direction.horizontal, sr.direction.vertical)
.ToVector3(); .ToVector3();
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-06); EXPECT_NEAR(r.horizontal, v.horizontal, 1.0e-06);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06); EXPECT_NEAR(r.vertical, v.vertical, 1.0e-06);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06); EXPECT_NEAR(r.depth, v.depth, 1.0e-06);
v = Vector3(0.0f, 1.0f, 1.0f); v = Vector3Of<float>(0.0f, 1.0f, 1.0f);
s = Spherical16::FromVector3(v); s = Spherical::FromVector3(v);
sr = Spherical16(1.4142135623F, Angle16::Degrees(0), Angle16::Degrees(45)); sr = Spherical(1.4142135623F, Angle::Degrees(0), Angle::Degrees(45));
EXPECT_NEAR(s.distance, sr.distance, 1.0e-05); EXPECT_NEAR(s.distance, sr.distance, 1.0e-05);
EXPECT_NEAR(s.direction.horizontal.InDegrees(), EXPECT_NEAR(s.direction.horizontal.InDegrees(),
sr.direction.horizontal.InDegrees(), 1.0e-05); sr.direction.horizontal.InDegrees(), 1.0e-05);
EXPECT_NEAR(s.direction.vertical.InDegrees(), EXPECT_NEAR(s.direction.vertical.InDegrees(),
sr.direction.vertical.InDegrees(), 1.0e-05); sr.direction.vertical.InDegrees(), 1.0e-05);
r = Spherical16(sr.distance, sr.direction.horizontal, sr.direction.vertical) r = Spherical(sr.distance, sr.direction.horizontal, sr.direction.vertical)
.ToVector3(); .ToVector3();
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-06); EXPECT_NEAR(r.horizontal, v.horizontal, 1.0e-06);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06); EXPECT_NEAR(r.vertical, v.vertical, 1.0e-06);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06); EXPECT_NEAR(r.depth, v.depth, 1.0e-06);
v = Vector3(1.0f, 1.0f, 1.0f); v = Vector3Of<float>(1.0f, 1.0f, 1.0f);
s = Spherical16::FromVector3(v); s = Spherical::FromVector3(v);
r = Spherical16(s.distance, s.direction.horizontal, s.direction.vertical) r = Spherical(s.distance, s.direction.horizontal, s.direction.vertical)
.ToVector3(); .ToVector3();
EXPECT_NEAR(s.distance, 1.73205080F, 1.0e-02); EXPECT_NEAR(s.distance, 1.73205080F, 1.0e-02);
EXPECT_NEAR(s.direction.horizontal.InDegrees(), 45.0F, 1.0e-02); EXPECT_NEAR(s.direction.horizontal.InDegrees(), 45.0F, 1.0e-02);
EXPECT_NEAR(s.direction.vertical.InDegrees(), 35.26F, 1.0e-02); EXPECT_NEAR(s.direction.vertical.InDegrees(), 35.26F, 1.0e-02);
EXPECT_NEAR(r.Right(), v.Right(), 1.0e-04); EXPECT_NEAR(r.horizontal, v.horizontal, 1.0e-04);
EXPECT_NEAR(r.Up(), v.Up(), 1.0e-04); EXPECT_NEAR(r.vertical, v.vertical, 1.0e-04);
EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-04); EXPECT_NEAR(r.depth, v.depth, 1.0e-04);
// s = Spherical16(10, 45, 45); // s = Spherical(10, 45, 45);
// r = s.ToVector3(); // r = s.ToVector3();
// EXPECT_NEAR(r.x, 5, 1.0e-06); // EXPECT_NEAR(r.x, 5, 1.0e-06);
// EXPECT_NEAR(r.y, 7.07, 1.0e-06); // EXPECT_NEAR(r.y, 7.07, 1.0e-06);
// EXPECT_NEAR(r.z, 5, 1.0e-06); // EXPECT_NEAR(r.z, 5, 1.0e-06);
} }
TEST(Spherical16, Addition) { TYPED_TEST(SphericalTests, Addition) {
Spherical16 v1 = Spherical16(1, Angle16::Degrees(45), Angle16::Degrees(0)); using T = TypeParam;
Spherical16 v2 = Spherical16::zero; using Spherical = SphericalOf<T>;
Spherical16 r = Spherical16::zero; using Angle = AngleOf<T>;
Spherical v1 = Spherical(1, Angle::Degrees(45), Angle::Degrees(0));
Spherical v2 = Spherical::zero;
Spherical r = Spherical::zero;
r = v1 + v2; r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)"; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)";
@ -170,36 +197,39 @@ TEST(Spherical16, Addition) {
r += v2; r += v2;
EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)"; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)";
v2 = Spherical16(1, Angle16::Degrees(-45), Angle16::Degrees(0)); v2 = Spherical(1, Angle::Degrees(-45), Angle::Degrees(0));
r = v1 + v2; r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 -45 0)"; EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 -45 0)";
EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 0) << "Addition(1 -45 0)"; EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 0) << "Addition(1 -45 0)";
EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 0) << "Addition(1 -45 0)"; EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 0) << "Addition(1 -45 0)";
v2 = Spherical16(1, Angle16::Degrees(0), Angle16::Degrees(90)); v2 = Spherical(1, Angle::Degrees(0), Angle::Degrees(90));
r = v1 + v2; r = v1 + v2;
EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 0 90)"; EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 0 90)";
EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 45) << "Addition(1 0 90)"; EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 45) << "Addition(1 0 90)";
EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 45) << "Addition(1 0 90)"; EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 45) << "Addition(1 0 90)";
} }
TEST(Spherical16, AdditionPerformance) { TYPED_TEST(SphericalTests, AdditionPerformance) {
using T = TypeParam;
using Spherical = SphericalOf<T>;
const int numIterations = 1000000; // Number of additions to test const int numIterations = 1000000; // Number of additions to test
std::vector<Spherical16> sphericalObjects; std::vector<Spherical> sphericalObjects;
// Populate the vector with random SphericalOf objects // Populate the vector with random SphericalOf objects
for (int i = 0; i < numIterations; ++i) { for (int i = 0; i < numIterations; ++i) {
float distance = (float)(rand() % 100); float distance = (float)(rand() % 100);
float horizontal = (float)(rand() % 180); float horizontal = (float)(rand() % 180);
float vertical = (float)(rand() % 360); float vertical = (float)(rand() % 360);
Spherical16 s = Spherical16::Deg(distance, horizontal, vertical); Spherical s = Spherical::Deg(distance, horizontal, vertical);
sphericalObjects.push_back(s); sphericalObjects.push_back(s);
} }
// Measure the time to perform multiple additions // Measure the time to perform multiple additions
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
Spherical16 result = Spherical16::zero; // Start with a Spherical result = Spherical::zero; // Start with a
// zero-initialized object // zero-initialized object
for (int i = 0; i < numIterations - 1; ++i) { for (int i = 0; i < numIterations - 1; ++i) {

View File

@ -7,125 +7,133 @@
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
TEST(SwingTwistSingle, Quaternion) { using BaseTypes = ::testing::Types<float, signed short>;
template <typename T>
class SwingTwistTests : public ::testing::Test {};
TYPED_TEST_SUITE(SwingTwistTests, BaseTypes);
TYPED_TEST(SwingTwistTests, Quaternion) {
using T = TypeParam;
using SwingTwist = SwingTwistOf<T>;
Quaternion q; Quaternion q;
SwingTwistSingle s; SwingTwist s;
Quaternion rq; Quaternion rq;
q = Quaternion::identity; q = Quaternion::identity;
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_EQ(q, rq) << " 0 0 0 1 <-> SwingTwist"; EXPECT_EQ(q, rq) << " 0 0 0 1 <-> SwingTwist";
q = Quaternion::Euler(90, 0, 0); q = Quaternion::Euler(90, 0, 0);
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 90 0 0 <-> SwingTwist"; EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 90 0 0 <-> SwingTwist";
q = Quaternion::Euler(0, 90, 0); q = Quaternion::Euler(0, 90, 0);
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist";
q = Quaternion::Euler(0, 0, 90); q = Quaternion::Euler(0, 0, 90);
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_EQ(q, rq) << " Euler 0 0 90 <-> SwingTwist"; EXPECT_EQ(q, rq) << " Euler 0 0 90 <-> SwingTwist";
q = Quaternion::Euler(0, 180, 0); // ==> spherical S(180 0)T0 q = Quaternion::Euler(0, 180, 0); // ==> spherical S(180 0)T0
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist";
q = Quaternion::Euler(0, 135, 0); // ==> spherical S(180 45)T0 q = Quaternion::Euler(0, 135, 0); // ==> spherical S(180 45)T0
s = SwingTwistSingle::FromQuaternion(q); s = SwingTwist::FromQuaternion(q);
rq = s.ToQuaternion(); rq = s.ToQuaternion();
EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist";
} }
TEST(SwingTwistSingle, AngleAxis) { TYPED_TEST(SwingTwistTests, AngleAxis) {
SwingTwistSingle s; using T = TypeParam;
SwingTwistSingle r; using SwingTwist = SwingTwistOf<T>;
using Direction = DirectionOf<T>;
using Angle = AngleOf<T>;
s = SwingTwistSingle::AngleAxis(0, DirectionSingle::up); SwingTwist s;
EXPECT_EQ(s, SwingTwistSingle::Degrees(0, 0, 0)) << "0 up"; SwingTwist r;
r = SwingTwistSingle::AngleAxis(90, DirectionSingle::up); s = SwingTwist::AngleAxis(0, Direction::up);
s = SwingTwistSingle::Degrees(90, 0, 0); EXPECT_EQ(s, SwingTwist::Degrees(0, 0, 0)) << "0 up";
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f))
<< "90 up";
r = SwingTwistSingle::AngleAxis(180, DirectionSingle::up); r = SwingTwist::AngleAxis(90, Direction::up);
s = SwingTwistSingle::Degrees(180, 0, 0); s = SwingTwist::Degrees(90, 0, 0);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "90 up";
<< "180 up";
r = SwingTwistSingle::AngleAxis(270, DirectionSingle::up); r = SwingTwist::AngleAxis(180, Direction::up);
s = SwingTwistSingle::Degrees(-90, 0, 0); s = SwingTwist::Degrees(180, 0, 0);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "180 up";
<< "270 up";
r = SwingTwistSingle::AngleAxis(90, DirectionSingle::right); r = SwingTwist::AngleAxis(270, Direction::up);
s = SwingTwistSingle::Degrees(0, 90, 0); s = SwingTwist::Degrees(-90, 0, 0);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "270 up";
r = SwingTwist::AngleAxis(90, Direction::right);
s = SwingTwist::Degrees(0, 90, 0);
EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f))
<< "90 right"; << "90 right";
r = SwingTwistSingle::AngleAxis(180, DirectionSingle::right); r = SwingTwist::AngleAxis(180, Direction::right);
s = SwingTwistSingle::Degrees(0, 180, 0); s = SwingTwist::Degrees(0, 180, 0);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f))
<< "180 right"; << "180 right";
r = SwingTwistSingle::AngleAxis(270, DirectionSingle::right); r = SwingTwist::AngleAxis(270, Direction::right);
s = SwingTwistSingle::Degrees(0, -90, 0); s = SwingTwist::Degrees(0, -90, 0);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f))
<< "270 right"; << "270 right";
r = SwingTwistSingle::AngleAxis(90, DirectionSingle::forward); r = SwingTwist::AngleAxis(90, Direction::forward);
s = SwingTwistSingle::Degrees(0, 0, 90); s = SwingTwist::Degrees(0, 0, 90);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "90 up";
<< "90 up";
r = SwingTwistSingle::AngleAxis(180, DirectionSingle::forward); r = SwingTwist::AngleAxis(180, Direction::forward);
s = SwingTwistSingle::Degrees(0, 0, 180); s = SwingTwist::Degrees(0, 0, 180);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "180 up";
<< "180 up";
r = SwingTwistSingle::AngleAxis(270, DirectionSingle::forward); r = SwingTwist::AngleAxis(270, Direction::forward);
s = SwingTwistSingle::Degrees(0, 0, -90); s = SwingTwist::Degrees(0, 0, -90);
EXPECT_LT(SwingTwistSingle::Angle(r, s), AngleSingle::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r, s), Angle::Degrees(10e-2f)) << "270 up";
<< "270 up";
auto r16 = SwingTwist16::AngleAxis(13, Direction16::down); auto r16 = SwingTwist::AngleAxis(13, Direction::down);
auto s16 = SwingTwist16::Degrees(-13, 0, 0); auto s16 = SwingTwist::Degrees(-13, 0, 0);
EXPECT_LT(SwingTwist16::Angle(r16, s16), Angle16::Degrees(10e-2f)) EXPECT_LT(SwingTwist::Angle(r16, s16), Angle::Degrees(10e-2f))
<< "270 up"; << "270 up";
} }
TEST(SwingTwistSingle, Normalize) { TYPED_TEST(SwingTwistTests, Normalize) {
SwingTwistSingle s; using T = TypeParam;
using SwingTwist = SwingTwistOf<T>;
s = SwingTwistSingle::Degrees(0, 0, 0); SwingTwist s;
EXPECT_EQ(s, SwingTwistSingle::Degrees(0, 0, 0)) << "0 0 0 Normalized";
s = SwingTwistSingle::Degrees(0, 180, 0); s = SwingTwist::Degrees(0, 0, 0);
EXPECT_EQ(s, SwingTwistSingle::Degrees(180, 0, 180)) << "0 180 0 Normalized"; EXPECT_EQ(s, SwingTwist::Degrees(0, 0, 0)) << "0 0 0 Normalized";
s = SwingTwistSingle::Degrees(0, 180, 180); s = SwingTwist::Degrees(0, 180, 0);
EXPECT_EQ(s, SwingTwistSingle::Degrees(180, 0, 0)) << "0 180 180 Normalized"; EXPECT_EQ(s, SwingTwist::Degrees(180, 0, 180)) << "0 180 0 Normalized";
s = SwingTwistSingle::Degrees(270, 90, 0); s = SwingTwist::Degrees(0, 180, 180);
EXPECT_EQ(s, SwingTwistSingle::Degrees(-90, 90, 0)) << "270 90 0 Normalized"; EXPECT_EQ(s, SwingTwist::Degrees(180, 0, 0)) << "0 180 180 Normalized";
s = SwingTwistSingle::Degrees(270, 270, 0); s = SwingTwist::Degrees(270, 90, 0);
EXPECT_EQ(s, SwingTwistSingle::Degrees(-90, -90, 0)) EXPECT_EQ(s, SwingTwist::Degrees(-90, 90, 0)) << "270 90 0 Normalized";
<< "270 270 0 Normalized";
s = SwingTwistSingle::Degrees(270, 225, 0); s = SwingTwist::Degrees(270, 270, 0);
EXPECT_EQ(s, SwingTwistSingle::Degrees(90, -45, -180)) EXPECT_EQ(s, SwingTwist::Degrees(-90, -90, 0)) << "270 270 0 Normalized";
<< "270 225 0 Normalized";
s = SwingTwistSingle::Degrees(270, 0, 225); s = SwingTwist::Degrees(270, 225, 0);
EXPECT_EQ(s, SwingTwistSingle::Degrees(-90, 0, -135)) EXPECT_EQ(s, SwingTwist::Degrees(90, -45, -180)) << "270 225 0 Normalized";
<< "270 0 225 Normalized";
s = SwingTwist::Degrees(270, 0, 225);
EXPECT_EQ(s, SwingTwist::Degrees(-90, 0, -135)) << "270 0 225 Normalized";
} }
#endif #endif

View File

@ -8,9 +8,21 @@
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
using Vector2 = Vector2Of<float>; using BaseTypes = ::testing::Types<float, int>;
using FpTypes = ::testing::Types<float>;
template <typename T>
class Vector2Tests : public ::testing::Test {};
TYPED_TEST_SUITE(Vector2Tests, BaseTypes);
template <typename T>
class Vector2FpTests : public ::testing::Test {};
TYPED_TEST_SUITE(Vector2FpTests, FpTypes);
TYPED_TEST(Vector2Tests, FromPolar) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
TEST(Vector2, FromPolar) {
Vector2 v; Vector2 v;
PolarSingle p; PolarSingle p;
Vector2Float r; Vector2Float r;
@ -37,7 +49,10 @@ TEST(Vector2, FromPolar) {
EXPECT_FLOAT_EQ(r.vertical, 0.0F) << "FromPolar(0 0)"; EXPECT_FLOAT_EQ(r.vertical, 0.0F) << "FromPolar(0 0)";
} }
TEST(Vector2, Magnitude) { TYPED_TEST(Vector2Tests, Magnitude) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v = Vector2(1, 2); Vector2 v = Vector2(1, 2);
float m = 0; float m = 0;
@ -54,6 +69,14 @@ TEST(Vector2, Magnitude) {
v = Vector2(0, 0); v = Vector2(0, 0);
m = v.Magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, 0) << "v.magnitude 0 0 "; EXPECT_FLOAT_EQ(m, 0) << "v.magnitude 0 0 ";
}
TYPED_TEST(Vector2FpTests, Magnitude) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
float m = 0;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -66,7 +89,10 @@ TEST(Vector2, Magnitude) {
} }
} }
TEST(Vector2, SqrMagnitude) { TYPED_TEST(Vector2Tests, SqrMagnitude) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v = Vector2(1, 2); Vector2 v = Vector2(1, 2);
float m = 0; float m = 0;
@ -83,6 +109,14 @@ TEST(Vector2, SqrMagnitude) {
v = Vector2(0, 0); v = Vector2(0, 0);
m = v.SqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, 0) << "v.sqrMagnitude 0 0 "; EXPECT_FLOAT_EQ(m, 0) << "v.sqrMagnitude 0 0 ";
}
TYPED_TEST(Vector2FpTests, SqrMagnitude) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
float m;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -95,7 +129,10 @@ TEST(Vector2, SqrMagnitude) {
} }
} }
TEST(Vector2, Normalize) { TYPED_TEST(Vector2Tests, Normalize) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
bool r = false; bool r = false;
Vector2 v1 = Vector2(0, 2); Vector2 v1 = Vector2(0, 2);
@ -114,6 +151,15 @@ TEST(Vector2, Normalize) {
v1 = Vector2(0, 0); v1 = Vector2(0, 0);
v = v1.Normalized(); v = v1.Normalized();
EXPECT_TRUE(v == Vector2(0, 0)) << "v.normalized 0 0"; EXPECT_TRUE(v == Vector2(0, 0)) << "v.normalized 0 0";
}
TYPED_TEST(Vector2FpTests, Normalize) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1;
float r;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v1 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v1 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -128,7 +174,10 @@ TEST(Vector2, Normalize) {
} }
} }
TEST(Vector2, Negate) { TYPED_TEST(Vector2Tests, Negate) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
@ -142,6 +191,14 @@ TEST(Vector2, Negate) {
v1 = Vector2(0, 0); v1 = Vector2(0, 0);
v = -v1; v = -v1;
EXPECT_TRUE(v == Vector2(0, 0)) << "- 0 0"; EXPECT_TRUE(v == Vector2(0, 0)) << "- 0 0";
}
TYPED_TEST(Vector2FpTests, Negate) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v1 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v1 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -156,7 +213,10 @@ TEST(Vector2, Negate) {
} }
} }
TEST(Vector2, Subtract) { TYPED_TEST(Vector2Tests, Subtract) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
@ -181,6 +241,15 @@ TEST(Vector2, Subtract) {
v -= v2; v -= v2;
EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 - 0 0"; EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 - 0 0";
}
TYPED_TEST(Vector2FpTests, Subtract) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -195,7 +264,10 @@ TEST(Vector2, Subtract) {
} }
} }
TEST(Vector2, Addition) { TYPED_TEST(Vector2Tests, Addition) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
@ -215,6 +287,15 @@ TEST(Vector2, Addition) {
EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 + 0 0"; EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 + 0 0";
v += v2; v += v2;
EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 + 0 0"; EXPECT_TRUE(v == Vector2(4, 5)) << "4 5 + 0 0";
}
TYPED_TEST(Vector2FpTests, Addition) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(0, 0);
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -229,7 +310,10 @@ TEST(Vector2, Addition) {
} }
} }
TEST(Vector2, Scale) { TYPED_TEST(Vector2Tests, Scale) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
@ -244,6 +328,15 @@ TEST(Vector2, Scale) {
v2 = Vector2(0, 0); v2 = Vector2(0, 0);
v = Vector2::Scale(v1, v2); v = Vector2::Scale(v1, v2);
EXPECT_TRUE(v == Vector2(0, 0)) << "Scale 4 5 , 0 0"; EXPECT_TRUE(v == Vector2(0, 0)) << "Scale 4 5 , 0 0";
}
TYPED_TEST(Vector2FpTests, Scale) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -258,9 +351,12 @@ TEST(Vector2, Scale) {
} }
} }
TEST(Vector2, Multiply) { TYPED_TEST(Vector2Tests, Multiply) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
float f = 3; T f = 3;
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
v = v1 * f; v = v1 * f;
@ -273,6 +369,15 @@ TEST(Vector2, Multiply) {
f = 0; f = 0;
v = v1 * f; v = v1 * f;
EXPECT_TRUE(v == Vector2(0, 0)) << "4 5 * 0"; EXPECT_TRUE(v == Vector2(0, 0)) << "4 5 * 0";
}
TYPED_TEST(Vector2FpTests, Multiply) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1 = Vector2(4, 5);
T f;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
f = FLOAT_INFINITY; f = FLOAT_INFINITY;
@ -287,9 +392,12 @@ TEST(Vector2, Multiply) {
} }
} }
TEST(Vector2, Divide) { TYPED_TEST(Vector2Tests, Divide) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
float f = 2; T f = 2;
Vector2 v = Vector2::zero; Vector2 v = Vector2::zero;
v = v1 / f; v = v1 / f;
@ -298,6 +406,15 @@ TEST(Vector2, Divide) {
f = -2; f = -2;
v = v1 / f; v = v1 / f;
EXPECT_TRUE(v == Vector2(-2, -2.5F)) << "4 5 / -3"; EXPECT_TRUE(v == Vector2(-2, -2.5F)) << "4 5 / -3";
}
TYPED_TEST(Vector2FpTests, Divide) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v;
Vector2 v1 = Vector2(4, 5);
T f;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
f = 0; f = 0;
@ -314,7 +431,10 @@ TEST(Vector2, Divide) {
} }
} }
TEST(Vector2, Dot) { TYPED_TEST(Vector2Tests, Dot) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
float f = 0; float f = 0;
@ -329,6 +449,15 @@ TEST(Vector2, Dot) {
v2 = Vector2(0, 0); v2 = Vector2(0, 0);
f = Vector2::Dot(v1, v2); f = Vector2::Dot(v1, v2);
EXPECT_FLOAT_EQ(f, 0) << "Dot(4 5, 0 0)"; EXPECT_FLOAT_EQ(f, 0) << "Dot(4 5, 0 0)";
}
TYPED_TEST(Vector2FpTests, Dot) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v2;
Vector2 v1 = Vector2(4, 5);
float f;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -341,7 +470,10 @@ TEST(Vector2, Dot) {
} }
} }
TEST(Vector2, Equality) { TYPED_TEST(Vector2Tests, Equality) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
bool r = false; bool r = false;
@ -352,6 +484,15 @@ TEST(Vector2, Equality) {
v2 = Vector2(4, 5); v2 = Vector2(4, 5);
r = v1 == v2; r = v1 == v2;
EXPECT_TRUE(r) << "4 5 == 1 2"; EXPECT_TRUE(r) << "4 5 == 1 2";
}
TYPED_TEST(Vector2FpTests, Equality) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
bool r;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -364,7 +505,10 @@ TEST(Vector2, Equality) {
} }
} }
TEST(Vector2, Distance) { TYPED_TEST(Vector2Tests, Distance) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
float f = 0; float f = 0;
@ -379,6 +523,15 @@ TEST(Vector2, Distance) {
v2 = Vector2(0, 0); v2 = Vector2(0, 0);
f = Vector2::Distance(v1, v2); f = Vector2::Distance(v1, v2);
EXPECT_FLOAT_EQ(f, 6.403124F) << "Distance(4 5, 0 0)"; EXPECT_FLOAT_EQ(f, 6.403124F) << "Distance(4 5, 0 0)";
}
TYPED_TEST(Vector2FpTests, Distance) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
float f;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -391,10 +544,14 @@ TEST(Vector2, Distance) {
} }
} }
TEST(Vector2, Angle) { TYPED_TEST(Vector2Tests, Angle) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
using Angle = AngleOf<float>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
AngleSingle f = AngleSingle::zero; Angle f = Angle::zero;
bool r = false; bool r = false;
f = Vector2::UnsignedAngle(v1, v2); f = Vector2::UnsignedAngle(v1, v2);
@ -407,6 +564,16 @@ TEST(Vector2, Angle) {
v2 = Vector2(0, 0); v2 = Vector2(0, 0);
f = Vector2::UnsignedAngle(v1, v2); f = Vector2::UnsignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "Angle(4 5, 0 0)"; EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "Angle(4 5, 0 0)";
}
TYPED_TEST(Vector2FpTests, Angle) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
AngleOf<float> f;
float r;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
@ -423,10 +590,14 @@ TEST(Vector2, Angle) {
} }
} }
TEST(Vector2, SignedAngle) { TYPED_TEST(Vector2Tests, SignedAngle) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
using Angle = AngleOf<float>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
AngleSingle f = AngleSingle::zero; Angle f = Angle::zero;
bool r = false; bool r = false;
f = Vector2::SignedAngle(v1, v2); f = Vector2::SignedAngle(v1, v2);
@ -440,6 +611,26 @@ TEST(Vector2, SignedAngle) {
f = Vector2::SignedAngle(v1, v2); f = Vector2::SignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "SignedAngle(4 5, 0 0)"; EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "SignedAngle(4 5, 0 0)";
v1 = Vector2(0, 1);
v2 = Vector2(1, 0);
f = Vector2::SignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 90.0F) << "SignedAngle(0 1, 1 0)";
v1 = Vector2(0, 1);
v2 = Vector2(0, -1);
f = Vector2::SignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 180.0F) << "SignedAngle(0 1, 1 0)";
}
TYPED_TEST(Vector2FpTests, SignedAngle) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5);
Vector2 v2;
AngleOf<float> f;
float r;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector2(FLOAT_INFINITY, FLOAT_INFINITY);
f = Vector2::SignedAngle(v1, v2); f = Vector2::SignedAngle(v1, v2);
@ -453,36 +644,33 @@ TEST(Vector2, SignedAngle) {
r = f.InDegrees() == 0; r = f.InDegrees() == 0;
EXPECT_TRUE(r) << "SignedAngle(4 5, -INFINITY -INFINITY)"; EXPECT_TRUE(r) << "SignedAngle(4 5, -INFINITY -INFINITY)";
} }
v1 = Vector2(0, 1);
v2 = Vector2(1, 0);
f = Vector2::SignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 90.0F) << "SignedAngle(0 1, 1 0)";
v1 = Vector2(0, 1);
v2 = Vector2(0, -1);
f = Vector2::SignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 180.0F) << "SignedAngle(0 1, 1 0)";
} }
TEST(Vector2, Rotate) { TYPED_TEST(Vector2Tests, Rotate) {
Vector2 v1 = Vector2(1, 2); using T = TypeParam;
Vector2 r = Vector2(0, 0); using Vector2 = Vector2Of<T>;
using Angle = AngleOf<float>;
r = Vector2::Rotate(v1, AngleSingle::Degrees(0)); Vector2 v1 = Vector2(1, 2);
Vector2Of<float> r = Vector2(0, 0);
r = Vector2::Rotate(v1, Angle::Degrees(0));
EXPECT_FLOAT_EQ(Vector2::Distance(r, v1), 0); EXPECT_FLOAT_EQ(Vector2::Distance(r, v1), 0);
r = Vector2::Rotate(v1, AngleSingle::Degrees(180)); r = Vector2::Rotate(v1, Angle::Degrees(180));
EXPECT_NEAR(Vector2::Distance(r, Vector2(-1, -2)), 0, 1.0e-06); EXPECT_NEAR(Vector2Of<float>::Distance(r, Vector2(-1, -2)), 0, 1.0e-06);
r = Vector2::Rotate(v1, AngleSingle::Degrees(-90)); r = Vector2::Rotate(v1, Angle::Degrees(-90));
EXPECT_NEAR(Vector2::Distance(r, Vector2(2, -1)), 0, 1.0e-06); EXPECT_NEAR(Vector2::Distance(r, Vector2(2, -1)), 0, 1.0e-06);
r = Vector2::Rotate(v1, AngleSingle::Degrees(270)); r = Vector2::Rotate(v1, Angle::Degrees(270));
EXPECT_NEAR(Vector2::Distance(r, Vector2(2, -1)), 0, 1.0e-06); EXPECT_NEAR(Vector2::Distance(r, Vector2(2, -1)), 0, 1.0e-06);
} }
TEST(Vector2, Lerp) { TYPED_TEST(Vector2Tests, Lerp) {
using T = TypeParam;
using Vector2 = Vector2Of<T>;
Vector2 v1 = Vector2(4, 5); Vector2 v1 = Vector2(4, 5);
Vector2 v2 = Vector2(1, 2); Vector2 v2 = Vector2(1, 2);
Vector2 r = Vector2(0, 0); Vector2 r = Vector2(0, 0);

View File

@ -1,134 +1,186 @@
#if GTEST #if GTEST
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <limits>
#include <math.h> #include <math.h>
#include <limits>
#include "Vector3.h" #include "Vector3.h"
#define FLOAT_INFINITY std::numeric_limits<float>::infinity() #define FLOAT_INFINITY std::numeric_limits<float>::infinity()
TEST(Vector3, FromSpherical) { using BaseTypes = ::testing::Types<float, int>;
using FpTypes = ::testing::Types<float>;
template <typename T>
class Vector3Tests : public ::testing::Test {};
TYPED_TEST_SUITE(Vector3Tests, BaseTypes);
template <typename T>
class Vector3FpTests : public ::testing::Test {};
TYPED_TEST_SUITE(Vector3FpTests, FpTypes);
TYPED_TEST(Vector3FpTests, FromSpherical) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v = Vector3(0, 0, 1); Vector3 v = Vector3(0, 0, 1);
SphericalOf<float> s = SphericalOf<float>::FromVector3(v); SphericalOf<float> s = SphericalOf<float>::FromVector3(v);
Vector3 r = Vector3(s); Vector3 r = Vector3::FromSpherical(s);
EXPECT_FLOAT_EQ(r.Right(), 0.0F) << "toVector3.x 0 0 1"; EXPECT_FLOAT_EQ(r.horizontal, 0.0F) << "toVector3.x 0 0 1";
EXPECT_NEAR(r.Up(), 0.0F, 1.0e-06) << "toVector3.y 0 0 1"; EXPECT_NEAR(r.vertical, 0.0F, 1.0e-06) << "toVector3.y 0 0 1";
EXPECT_FLOAT_EQ(r.Forward(), 1.0F) << "toVector3.z 0 0 1"; EXPECT_FLOAT_EQ(r.depth, 1.0F) << "toVector3.z 0 0 1";
v = Vector3(0, 1, 0); v = Vector3(0, 1, 0);
s = SphericalOf<float>::FromVector3(v); s = SphericalOf<float>::FromVector3(v);
r = Vector3(s); r = Vector3::FromSpherical(s);
EXPECT_FLOAT_EQ(r.Right(), 0.0F) << "toVector3.x 0 1 0"; EXPECT_FLOAT_EQ(r.horizontal, 0.0F) << "toVector3.x 0 1 0";
EXPECT_FLOAT_EQ(r.Up(), 1.0F) << "toVector3.y 0 1 0"; EXPECT_FLOAT_EQ(r.vertical, 1.0F) << "toVector3.y 0 1 0";
EXPECT_NEAR(r.Forward(), 0.0F, 1.0e-06) << "toVector3.z 0 1 0"; EXPECT_NEAR(r.depth, 0.0F, 1.0e-06) << "toVector3.z 0 1 0";
v = Vector3(1, 0, 0); v = Vector3(1, 0, 0);
s = SphericalOf<float>::FromVector3(v); s = SphericalOf<float>::FromVector3(v);
r = Vector3(s); r = Vector3::FromSpherical(s);
EXPECT_FLOAT_EQ(r.Right(), 1.0F) << "toVector3.x 1 0 0"; EXPECT_FLOAT_EQ(r.horizontal, 1.0F) << "toVector3.x 1 0 0";
EXPECT_NEAR(r.Up(), 0.0F, 1.0e-06) << "toVector3.y 1 0 0"; EXPECT_NEAR(r.vertical, 0.0F, 1.0e-06) << "toVector3.y 1 0 0";
EXPECT_NEAR(r.Forward(), 0.0F, 1.0e-06) << "toVector3.z 1 0 0"; EXPECT_NEAR(r.depth, 0.0F, 1.0e-06) << "toVector3.z 1 0 0";
} }
TEST(Vector3, Magnitude) { TYPED_TEST(Vector3Tests, Magnitude) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v = Vector3(1, 2, 3); Vector3 v = Vector3(1, 2, 3);
float m = 0; float m = 0;
m = v.magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, 3.741657F) << "v.magnitude 1 2 3"; EXPECT_FLOAT_EQ(m, 3.741657F) << "v.magnitude 1 2 3";
m = Vector3::Magnitude(v); m = Vector3::MagnitudeOf(v);
EXPECT_FLOAT_EQ(m, 3.741657F) << "Vector3::Magnitude 1 2 3"; EXPECT_FLOAT_EQ(m, 3.741657F) << "Vector3::Magnitude 1 2 3";
v = Vector3(-1, -2, -3); v = Vector3(-1, -2, -3);
m = v.magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, 3.741657F) << "v.magnitude -1 -2 -3"; EXPECT_FLOAT_EQ(m, 3.741657F) << "v.magnitude -1 -2 -3";
v = Vector3(0, 0, 0); v = Vector3(0, 0, 0);
m = v.magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, 0) << "v.magnitude 0 0 0 "; EXPECT_FLOAT_EQ(m, 0) << "v.magnitude 0 0 0 ";
}
TYPED_TEST(Vector3FpTests, Magnitude) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v;
float m;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
m = v.magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, FLOAT_INFINITY) EXPECT_FLOAT_EQ(m, FLOAT_INFINITY)
<< "v.magnitude INFINITY INFINITY INFINITY "; << "v.magnitude INFINITY INFINITY INFINITY ";
v = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
m = v.magnitude(); m = v.Magnitude();
EXPECT_FLOAT_EQ(m, FLOAT_INFINITY) EXPECT_FLOAT_EQ(m, FLOAT_INFINITY)
<< "v.magnitude -INFINITY -INFINITY -INFINITY "; << "v.magnitude -INFINITY -INFINITY -INFINITY ";
} }
} }
TEST(Vector3, SqrMagnitude) { TYPED_TEST(Vector3Tests, SqrMagnitude) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v = Vector3(1, 2, 3); Vector3 v = Vector3(1, 2, 3);
float m = 0; float m = 0;
m = v.sqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, 14) << "v.sqrMagnitude 1 2 3"; EXPECT_FLOAT_EQ(m, 14) << "v.sqrMagnitude 1 2 3";
m = Vector3::SqrMagnitude(v); m = Vector3::SqrMagnitudeOf(v);
EXPECT_FLOAT_EQ(m, 14) << "Vector3::SqrMagnitude 1 2 3"; EXPECT_FLOAT_EQ(m, 14) << "Vector3::SqrMagnitude 1 2 3";
v = Vector3(-1, -2, -3); v = Vector3(-1, -2, -3);
m = v.sqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, 14) << "v.sqrMagnitude -1 -2 -3"; EXPECT_FLOAT_EQ(m, 14) << "v.sqrMagnitude -1 -2 -3";
v = Vector3(0, 0, 0); v = Vector3(0, 0, 0);
m = v.sqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, 0) << "v.sqrMagnitude 0 0 0 "; EXPECT_FLOAT_EQ(m, 0) << "v.sqrMagnitude 0 0 0 ";
}
TYPED_TEST(Vector3FpTests, SqrMagnitude) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v;
float m;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
m = v.sqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, FLOAT_INFINITY) EXPECT_FLOAT_EQ(m, FLOAT_INFINITY)
<< "v.sqrMagnitude INFINITY INFINITY INFINITY "; << "v.sqrMagnitude INFINITY INFINITY INFINITY ";
v = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
m = v.sqrMagnitude(); m = v.SqrMagnitude();
EXPECT_FLOAT_EQ(m, FLOAT_INFINITY) EXPECT_FLOAT_EQ(m, FLOAT_INFINITY)
<< "v.sqrMagnitude -INFINITY -INFINITY -INFINITY "; << "v.sqrMagnitude -INFINITY -INFINITY -INFINITY ";
} }
} }
TEST(Vector3, Normalize) { TYPED_TEST(Vector3Tests, Normalize) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
bool r = false; bool r = false;
Vector3 v1 = Vector3(0, 2, 0); Vector3 v1 = Vector3(0, 2, 0);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
v = v1.normalized(); v = v1.Normalized();
EXPECT_TRUE(v == Vector3(0, 1, 0)) << "v.normalized 0 2 0"; EXPECT_TRUE(v == Vector3(0, 1, 0)) << "v.normalized 0 2 0";
v = Vector3::Normalize(v1); v = Vector3::Normalize(v1);
EXPECT_TRUE(v == Vector3(0, 1, 0)) << "Vector3::Normalize 0 2 0"; EXPECT_TRUE(v == Vector3(0, 1, 0)) << "Vector3::Normalize 0 2 0";
v1 = Vector3(0, -2, 0); v1 = Vector3(0, -2, 0);
v = v1.normalized(); v = v1.Normalized();
EXPECT_TRUE(v == Vector3(0, -1, 0)) << "v.normalized 0 -2 0"; EXPECT_TRUE(v == Vector3(0, -1, 0)) << "v.normalized 0 -2 0";
v1 = Vector3(0, 0, 0); v1 = Vector3(0, 0, 0);
v = v1.normalized(); v = v1.Normalized();
EXPECT_TRUE(v == Vector3(0, 0, 0)) << "v.normalized 0 0 0"; EXPECT_TRUE(v == Vector3(0, 0, 0)) << "v.normalized 0 0 0";
}
TYPED_TEST(Vector3FpTests, Normalize) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
bool r = false;
Vector3 v1 = Vector3(0, 2, 0);
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v1 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v1 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
v = v1.normalized(); v = v1.Normalized();
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "v.normalized INFINITY INFINITY INFINITY"; EXPECT_TRUE(r) << "v.normalized INFINITY INFINITY INFINITY";
v1 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v1 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
v = v1.normalized(); v = v1.Normalized();
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "v.normalized -INFINITY -INFINITY -INFINITY"; EXPECT_TRUE(r) << "v.normalized -INFINITY -INFINITY -INFINITY";
} }
} }
TEST(Vector3, Negate) { TYPED_TEST(Vector3Tests, Negate) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -142,6 +194,14 @@ TEST(Vector3, Negate) {
v1 = Vector3(0, 0, 0); v1 = Vector3(0, 0, 0);
v = -v1; v = -v1;
EXPECT_TRUE(v == Vector3(0, 0, 0)) << "- 0 0 0"; EXPECT_TRUE(v == Vector3(0, 0, 0)) << "- 0 0 0";
}
TYPED_TEST(Vector3FpTests, Negate) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v1 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v1 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -156,7 +216,11 @@ TEST(Vector3, Negate) {
} }
} }
TEST(Vector3, Subtract) { TYPED_TEST(Vector3Tests, Subtract) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -175,6 +239,15 @@ TEST(Vector3, Subtract) {
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
v = v1 - v2; v = v1 - v2;
EXPECT_TRUE(v == Vector3(4, 5, 6)) << "4 5 6 - 0 0 0"; EXPECT_TRUE(v == Vector3(4, 5, 6)) << "4 5 6 - 0 0 0";
}
TYPED_TEST(Vector3FpTests, Subtract) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -189,7 +262,10 @@ TEST(Vector3, Subtract) {
} }
} }
TEST(Vector3, Addition) { TYPED_TEST(Vector3Tests, Addition) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -204,6 +280,15 @@ TEST(Vector3, Addition) {
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
v = v1 + v2; v = v1 + v2;
EXPECT_TRUE(v == Vector3(4, 5, 6)) << "4 5 6 + 0 0 0"; EXPECT_TRUE(v == Vector3(4, 5, 6)) << "4 5 6 + 0 0 0";
}
TYPED_TEST(Vector3FpTests, Addition) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -218,7 +303,11 @@ TEST(Vector3, Addition) {
} }
} }
TEST(Vector3, Scale) { TYPED_TEST(Vector3Tests, Scale) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -233,6 +322,15 @@ TEST(Vector3, Scale) {
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
v = Vector3::Scale(v1, v2); v = Vector3::Scale(v1, v2);
EXPECT_TRUE(v == Vector3(0, 0, 0)) << "Scale 4 5 6 , 0 0 0"; EXPECT_TRUE(v == Vector3(0, 0, 0)) << "Scale 4 5 6 , 0 0 0";
}
TYPED_TEST(Vector3FpTests, Scale) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -247,7 +345,10 @@ TEST(Vector3, Scale) {
} }
} }
TEST(Vector3, Multiply) { TYPED_TEST(Vector3Tests, Multiply) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
float f = 3; float f = 3;
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -262,6 +363,15 @@ TEST(Vector3, Multiply) {
f = 0; f = 0;
v = v1 * f; v = v1 * f;
EXPECT_TRUE(v == Vector3(0, 0, 0)) << "4 5 6 * 0"; EXPECT_TRUE(v == Vector3(0, 0, 0)) << "4 5 6 * 0";
}
TYPED_TEST(Vector3FpTests, Multiply) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
float f = 3;
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
f = FLOAT_INFINITY; f = FLOAT_INFINITY;
@ -276,7 +386,10 @@ TEST(Vector3, Multiply) {
} }
} }
TEST(Vector3, Divide) { TYPED_TEST(Vector3Tests, Divide) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
float f = 2; float f = 2;
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -287,6 +400,16 @@ TEST(Vector3, Divide) {
f = -2; f = -2;
v = v1 / f; v = v1 / f;
EXPECT_TRUE(v == Vector3(-2, -2.5F, -3)) << "4 5 6 / -3"; EXPECT_TRUE(v == Vector3(-2, -2.5F, -3)) << "4 5 6 / -3";
}
TYPED_TEST(Vector3FpTests, Divide) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
float f = 2;
Vector3 v = Vector3::zero;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
f = 0; f = 0;
@ -304,7 +427,10 @@ TEST(Vector3, Divide) {
} }
} }
TEST(Vector3, Dot) { TYPED_TEST(Vector3Tests, Dot) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
float f = 0; float f = 0;
@ -319,6 +445,15 @@ TEST(Vector3, Dot) {
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
f = Vector3::Dot(v1, v2); f = Vector3::Dot(v1, v2);
EXPECT_FLOAT_EQ(f, 0) << "Dot(4 5 6, 0 0 0)"; EXPECT_FLOAT_EQ(f, 0) << "Dot(4 5 6, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, Dot) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
float f = 0;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -333,7 +468,10 @@ TEST(Vector3, Dot) {
} }
} }
TEST(Vector3, Equality) { TYPED_TEST(Vector3Tests, Equality) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
bool r = false; bool r = false;
@ -344,6 +482,15 @@ TEST(Vector3, Equality) {
v2 = Vector3(4, 5, 6); v2 = Vector3(4, 5, 6);
r = v1 == v2; r = v1 == v2;
EXPECT_TRUE(r) << "4 5 6 == 1 2 3"; EXPECT_TRUE(r) << "4 5 6 == 1 2 3";
}
TYPED_TEST(Vector3FpTests, Equality) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
bool r = false;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -357,7 +504,10 @@ TEST(Vector3, Equality) {
} }
} }
TEST(Vector3, Distance) { TYPED_TEST(Vector3Tests, Distance) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
float f = 0; float f = 0;
@ -372,6 +522,15 @@ TEST(Vector3, Distance) {
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
f = Vector3::Distance(v1, v2); f = Vector3::Distance(v1, v2);
EXPECT_FLOAT_EQ(f, 8.77496433F) << "Distance(4 5 6, 0 0 0)"; EXPECT_FLOAT_EQ(f, 8.77496433F) << "Distance(4 5 6, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, Distance) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
float f = 0;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -386,7 +545,10 @@ TEST(Vector3, Distance) {
} }
} }
TEST(Vector3, Cross) { TYPED_TEST(Vector3Tests, Cross) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -405,21 +567,34 @@ TEST(Vector3, Cross) {
v = Vector3::Cross(v1, v2); v = Vector3::Cross(v1, v2);
r = v == Vector3(0, 0, 0); r = v == Vector3(0, 0, 0);
EXPECT_TRUE(r) << "Cross(4 5 6, 0 0 0)"; EXPECT_TRUE(r) << "Cross(4 5 6, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, Cross) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero;
bool r = false;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
v = Vector3::Cross(v1, v2); v = Vector3::Cross(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "Cross(4 5 6, INFINITY INFINITY INFINITY)"; EXPECT_TRUE(r) << "Cross(4 5 6, INFINITY INFINITY INFINITY)";
v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
v = Vector3::Cross(v1, v2); v = Vector3::Cross(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "Cross(4 5 6, -INFINITY -INFINITY -INFINITY)"; EXPECT_TRUE(r) << "Cross(4 5 6, -INFINITY -INFINITY -INFINITY)";
} }
} }
TEST(Vector3, Project) { TYPED_TEST(Vector3Tests, Project) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
@ -438,84 +613,124 @@ TEST(Vector3, Project) {
v = Vector3::Project(v1, v2); v = Vector3::Project(v1, v2);
r = v == Vector3(0, 0, 0); r = v == Vector3(0, 0, 0);
EXPECT_TRUE(r) << "Project(4 5 6, 0 0 0)"; EXPECT_TRUE(r) << "Project(4 5 6, 0 0 0)";
if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
v = Vector3::Project(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward());
EXPECT_TRUE(r) << "Project(4 5 6, INFINITY INFINITY INFINITY)";
v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
v = Vector3::Project(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward());
EXPECT_TRUE(r) << "Project(4 5 6, -INFINITY -INFINITY -INFINITY)";
}
} }
TEST(Vector3, ProjectOnPlane) { TYPED_TEST(Vector3FpTests, Project) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero; Vector3 v = Vector3::zero;
bool r = false; bool r = false;
if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
v = Vector3::Project(v1, v2);
r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "Project(4 5 6, INFINITY INFINITY INFINITY)";
v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
v = Vector3::Project(v1, v2);
r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "Project(4 5 6, -INFINITY -INFINITY -INFINITY)";
}
}
TYPED_TEST(Vector3Tests, ProjectOnPlane) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3Of<float> v = Vector3Of<float>::zero;
bool r = false;
v = Vector3::ProjectOnPlane(v1, v2); v = Vector3::ProjectOnPlane(v1, v2);
r = v == Vector3(1.71428561F, 0.428571224F, -0.857142925F); r = v == Vector3Of<float>(1.71428561F, 0.428571224F, -0.857142925F);
EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, 1 2 3)"; EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, 1 2 3)";
//EXPECT_EQ(v, Vector3(1.71428561F, 0.428571224F, -0.857142925F));
v2 = Vector3(-1, -2, -3); v2 = Vector3(-1, -2, -3);
v = Vector3::ProjectOnPlane(v1, v2); v = Vector3::ProjectOnPlane(v1, v2);
r = v == Vector3(1.71428561F, 0.428571224F, -0.857142925F); r = v == Vector3Of<float>(1.71428561F, 0.428571224F, -0.857142925F);
EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, -1 -2 -3)"; EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, -1 -2 -3)";
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
v = Vector3::ProjectOnPlane(v1, v2); v = Vector3::ProjectOnPlane(v1, v2);
r = v == Vector3(4, 5, 6); r = v == Vector3(4, 5, 6);
EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, 0 0 0)"; EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, ProjectOnPlane) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v = Vector3::zero;
bool r = false;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
v = Vector3::ProjectOnPlane(v1, v2); v = Vector3::ProjectOnPlane(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, INFINITY INFINITY INFINITY)"; EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, INFINITY INFINITY INFINITY)";
v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
v = Vector3::ProjectOnPlane(v1, v2); v = Vector3::ProjectOnPlane(v1, v2);
r = isnan(v.Right()) && isnan(v.Up()) && isnan(v.Forward()); r = isnan(v.horizontal) && isnan(v.vertical) && isnan(v.depth);
EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, -INFINITY -INFINITY -INFINITY)"; EXPECT_TRUE(r) << "ProjectOnPlane(4 5 6, -INFINITY -INFINITY -INFINITY)";
} }
} }
TEST(Vector3, Angle) { TYPED_TEST(Vector3Tests, Angle) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
AngleOf<float> f = AngleOf<float>::Degrees(0); AngleOf<float> f = AngleOf<float>::Degrees(0);
bool r = false; bool r = false;
f = Vector3::Angle(v1, v2); f = Vector3::UnsignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 12.9331388F) << "Angle(4 5 6, 1 2 3)"; EXPECT_FLOAT_EQ(f.InDegrees(), 12.9331388F) << "Angle(4 5 6, 1 2 3)";
v2 = Vector3(-1, -2, -3); v2 = Vector3(-1, -2, -3);
f = Vector3::Angle(v1, v2); f = Vector3::UnsignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 167.066864F) << "Angle(4 5 6, -1 -2 -3)"; EXPECT_FLOAT_EQ(f.InDegrees(), 167.066864F) << "Angle(4 5 6, -1 -2 -3)";
v2 = Vector3(0, 0, 0); v2 = Vector3(0, 0, 0);
f = Vector3::Angle(v1, v2); f = Vector3::UnsignedAngle(v1, v2);
EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "Angle(4 5 6, 0 0 0)"; EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "Angle(4 5 6, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, Angle) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
AngleOf<float> f = AngleOf<float>::Degrees(0);
bool r = false;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
f = Vector3::Angle(v1, v2); f = Vector3::UnsignedAngle(v1, v2);
r = isnan(f.InDegrees()); r = isnan(f.InDegrees());
EXPECT_TRUE(r) << "Angle(4 5 6, INFINITY INFINITY INFINITY)"; EXPECT_TRUE(r) << "Angle(4 5 6, INFINITY INFINITY INFINITY)";
v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY); v2 = Vector3(-FLOAT_INFINITY, -FLOAT_INFINITY, -FLOAT_INFINITY);
f = Vector3::Angle(v1, v2); f = Vector3::UnsignedAngle(v1, v2);
r = isnan(f.InDegrees()); r = isnan(f.InDegrees());
EXPECT_TRUE(r) << "Angle(4 5 6, -INFINITY -INFINITY -INFINITY)"; EXPECT_TRUE(r) << "Angle(4 5 6, -INFINITY -INFINITY -INFINITY)";
} }
} }
TEST(Vector3, SignedAngle) { TYPED_TEST(Vector3Tests, SignedAngle) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 v3 = Vector3(7, 8, -9); Vector3 v3 = Vector3(7, 8, -9);
@ -545,6 +760,17 @@ TEST(Vector3, SignedAngle) {
v3 = Vector3(0, 0, 0); v3 = Vector3(0, 0, 0);
f = Vector3::SignedAngle(v1, v2, v3); f = Vector3::SignedAngle(v1, v2, v3);
EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "SignedAngle(4 5 6, 1 2 3, 0 0 0)"; EXPECT_FLOAT_EQ(f.InDegrees(), 0) << "SignedAngle(4 5 6, 1 2 3, 0 0 0)";
}
TYPED_TEST(Vector3FpTests, SignedAngle) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3);
Vector3 v3 = Vector3(7, 8, -9);
AngleOf<float> f = AngleOf<float>::Degrees(0);
bool r = false;
if (std::numeric_limits<float>::is_iec559) { if (std::numeric_limits<float>::is_iec559) {
v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY); v2 = Vector3(FLOAT_INFINITY, FLOAT_INFINITY, FLOAT_INFINITY);
@ -559,7 +785,11 @@ TEST(Vector3, SignedAngle) {
} }
} }
TEST(Vector3, Lerp) { TYPED_TEST(Vector3Tests, Lerp) {
using T = TypeParam;
using Vector3 = Vector3Of<T>;
;
Vector3 v1 = Vector3(4, 5, 6); Vector3 v1 = Vector3(4, 5, 6);
Vector3 v2 = Vector3(1, 2, 3); Vector3 v2 = Vector3(1, 2, 3);
Vector3 r = Vector3(0, 0, 0); Vector3 r = Vector3(0, 0, 0);