diff --git a/Engine.Core/Primitives/Vector4D.cs b/Engine.Core/Primitives/Vector4D.cs new file mode 100644 index 0000000..4e38feb --- /dev/null +++ b/Engine.Core/Primitives/Vector4D.cs @@ -0,0 +1,318 @@ + +using System; + +namespace Engine.Core; + +/// +/// Represents a four-dimensional vector. +/// +/// X position of the . +/// Y position of the . +/// Z position of the . +/// W position of the . +/// +/// Initializes a new instance of the struct with the specified positions. +/// +[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] +public readonly struct Vector4D(float x, float y, float z, float w) : IEquatable +{ + /// + /// The X coordinate of the . + /// + public readonly float X = x; + + /// + /// The Y coordinate of the . + /// + public readonly float Y = y; + + /// + /// The Z coordinate of the . + /// + public readonly float Z = z; + + /// + /// The W coordinate of the . + /// + public readonly float W = w; + + /// + /// The magnitude (length) of the . + /// + public float Magnitude => Length(this); + + /// + /// The squared magnitude (length) of the . + /// + public float MagnitudeSquared => LengthSquared(this); + + /// + /// The normalized form of the (a with the same direction and a magnitude of 1). + /// + public Vector4D Normalized => Normalize(this); + + /// + /// Represents the zero . + /// + public readonly static Vector4D Zero = new(0f, 0f, 0f, 0f); + + /// + /// Represents the one . + /// + public readonly static Vector4D One = new(1f, 1f, 1f, 1f); + + /// + /// Represents the unit X . + /// + public readonly static Vector4D UnitX = new(1f, 0f, 0f, 0f); + + /// + /// Represents the unit Y . + /// + public readonly static Vector4D UnitY = new(0f, 1f, 0f, 0f); + + /// + /// Represents the unit Z . + /// + public readonly static Vector4D UnitZ = new(0f, 0f, 1f, 0f); + + /// + /// Represents the unit W . + /// + public readonly static Vector4D UnitW = new(0f, 0f, 0f, 1f); + + public static Vector4D operator -(Vector4D vector) => new(0f - vector.X, 0f - vector.Y, 0f - vector.Z, 0f - vector.W); + public static Vector4D operator +(Vector4D left, Vector4D right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + public static Vector4D operator -(Vector4D left, Vector4D right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + public static Vector4D operator *(Vector4D vector, float value) => new(vector.X * value, vector.Y * value, vector.Z * value, vector.W * value); + public static Vector4D operator *(float value, Vector4D vector) => new(vector.X * value, vector.Y * value, vector.Z * value, vector.W * value); + public static Vector4D operator /(Vector4D vector, float value) => new(vector.X / value, vector.Y / value, vector.Z / value, vector.W / value); + public static bool operator ==(Vector4D left, Vector4D right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W; + public static bool operator !=(Vector4D left, Vector4D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W; + + public static implicit operator System.Numerics.Vector4(Vector4D vector) => new(vector.X, vector.Y, vector.Z, vector.W); + public static implicit operator Vector4D(System.Numerics.Vector4 vector) => new(vector.X, vector.Y, vector.Z, vector.W); + + /// + /// Calculates the length of the . + /// + /// The . + /// The length of the . + public static float Length(Vector4D vector) => Math.Sqrt(LengthSquared(vector)); + + /// + /// Calculates the squared length of the . + /// + /// The . + /// The squared length of the . + public static float LengthSquared(Vector4D vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z + vector.W * vector.W; + + /// + /// Calculates the distance between two s. + /// + /// The start . + /// The end . + /// The distance between the two s. + public static float Distance(Vector4D from, Vector4D to) => Length(FromTo(from, to)); + + /// + /// Inverts the direction of the . + /// + /// The . + /// The inverted . + public static Vector4D Invert(Vector4D vector) => -vector; + + /// + /// Adds two s. + /// + /// The first . + /// The second . + /// The sum of the two s. + public static Vector4D Add(Vector4D left, Vector4D right) => left + right; + + /// + /// Subtracts one from another. + /// + /// The to subtract from. + /// The to subtract. + /// The result of subtracting the second from the first. + public static Vector4D Subtract(Vector4D left, Vector4D right) => left - right; + + /// + /// Multiplies a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of multiplying the by the scalar value. + public static Vector4D Multiply(Vector4D vector, float value) => vector * value; + + /// + /// Divides a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of dividing the by the scalar value. + public static Vector4D Divide(Vector4D vector, float value) => vector / value; + + /// + /// Calculates the absolute value of each component of the vector. + /// + /// The . + /// The with each component's absolute value. + public static Vector4D Abs(Vector4D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z), Math.Abs(vector.W)); + + /// + /// Normalizes the (creates a unit with the same direction). + /// + /// The to normalize. + /// The normalized . + public static Vector4D Normalize(Vector4D vector) => vector / Length(vector); + + /// + /// Calculates the from one point to another. + /// + /// The starting point. + /// The ending point. + /// The from the starting point to the ending point. + public static Vector4D FromTo(Vector4D from, Vector4D to) => to - from; + + /// + /// Scales a by another component-wise. + /// + /// The to scale. + /// The containing the scaling factors for each component. + /// The scaled . + public static Vector4D Scale(Vector4D vector, Vector4D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z, vector.W * scale.W); + + /// + /// Returns the component-wise minimum of two s. + /// + /// The first . + /// The second . + /// The containing the minimum components from both input s. + public static Vector4D Min(Vector4D left, Vector4D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y, (left.Z < right.Z) ? left.Z : right.Z, (left.W < right.W) ? left.W : right.W); + + /// + /// Returns the component-wise maximum of two s. + /// + /// The first . + /// The second . + /// The containing the maximum components from both input s. + public static Vector4D Max(Vector4D left, Vector4D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y, (left.Z > right.Z) ? left.Z : right.Z, (left.W > right.W) ? left.W : right.W); + + /// + /// Clamps each component of a between the corresponding component of two other s. + /// + /// The to clamp. + /// The representing the minimum values for each component. + /// The representing the maximum values for each component. + /// A with each component clamped between the corresponding components of the min and max s. + public static Vector4D Clamp(Vector4D vector, Vector4D min, Vector4D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y), Math.Clamp(vector.Z, min.Z, max.Z), Math.Clamp(vector.W, min.W, max.W)); + + /// + /// Performs linear interpolation between two s. + /// + /// The starting (t = 0). + /// The ending (t = 1). + /// The interpolation parameter. + /// The interpolated . + public static Vector4D Lerp(Vector4D from, Vector4D to, float t) => from + FromTo(from, to) * t; + + /// + /// Calculates the dot product of two s. + /// + /// The first . + /// The second . + /// The dot product of the two s. + public static float Dot(Vector4D left, Vector4D right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W; + + /// + /// Checks if two s are approximately equal within a specified epsilon range. + /// + /// The first . + /// The second . + /// The epsilon range. + /// if the s are approximately equal; otherwise, . + public static bool ApproximatelyEquals(Vector4D left, Vector4D right, float epsilon = float.Epsilon) + => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.Z.ApproximatelyEquals(right.W, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Vector4D vector4D && this == vector4D; + public bool Equals(Vector4D other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(X, Y, Z, W); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Vector4D)}({X}, {Y}, {Z}, {W})"; +} + +/// +/// Provides extension methods for type. +/// +public static class Vector4DExtensions +{ + /// + public static float Length(this Vector4D vector) => Vector4D.Length(vector); + + /// + public static float LengthSquared(this Vector4D vector) => Vector4D.LengthSquared(vector); + + /// + public static float Distance(this Vector4D from, Vector4D to) => Vector4D.Distance(from, to); + + /// + public static Vector4D Invert(this Vector4D vector) => Vector4D.Invert(vector); + + /// + public static Vector4D Add(this Vector4D vector, Vector4D vectorToAdd) => Vector4D.Add(vector, vectorToAdd); + + /// + public static Vector4D Subtract(this Vector4D vector, Vector4D vectorToSubtract) => Vector4D.Subtract(vector, vectorToSubtract); + + /// + public static Vector4D Multiply(this Vector4D vector, float value) => Vector4D.Multiply(vector, value); + + /// + public static Vector4D Divide(this Vector4D vector, float value) => Vector4D.Divide(vector, value); + + /// + public static Vector4D Abs(this Vector4D vector) => Vector4D.Abs(vector); + + /// + public static Vector4D Normalize(this Vector4D vector) => Vector4D.Normalize(vector); + + /// + public static Vector4D FromTo(this Vector4D from, Vector4D to) => Vector4D.FromTo(from, to); + + /// + public static Vector4D Scale(this Vector4D vector, Vector4D scale) => Vector4D.Scale(vector, scale); + + /// + public static Vector4D Min(this Vector4D left, Vector4D right) => Vector4D.Min(left, right); + + /// + public static Vector4D Max(this Vector4D left, Vector4D right) => Vector4D.Max(left, right); + + /// + public static Vector4D Clamp(this Vector4D vector, Vector4D min, Vector4D max) => Vector4D.Clamp(vector, min, max); + + /// + public static Vector4D Lerp(this Vector4D from, Vector4D to, float t) => Vector4D.Lerp(from, to, t); + + /// + public static float Dot(this Vector4D left, Vector4D right) => Vector4D.Dot(left, right); + + /// + public static bool ApproximatelyEquals(this Vector4D left, Vector4D right, float epsilon = float.Epsilon) => Vector4D.ApproximatelyEquals(left, right, epsilon); +}