using System; namespace Syntriax.Engine.Core; /// <summary> /// Represents a three-dimensional vector. /// </summary> /// <param name="x">X position of the <see cref="Vector3D"/>.</param> /// <param name="y">Y position of the <see cref="Vector3D"/>.</param> /// <param name="z">Z position of the <see cref="Vector3D"/>.</param> /// <remarks> /// Initializes a new instance of the <see cref="Vector3D"/> struct with the specified positions. /// </remarks> [System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] public readonly struct Vector3D(float x, float y, float z) { /// <summary> /// The X coordinate of the <see cref="Vector3D"/>. /// </summary> public readonly float X = x; /// <summary> /// The Y coordinate of the <see cref="Vector3D"/>. /// </summary> public readonly float Y = y; /// <summary> /// The Z coordinate of the <see cref="Vector3D"/>. /// </summary> public readonly float Z = z; /// <summary> /// The magnitude (length) of the <see cref="Vector3D"/>. /// </summary> public float Magnitude => Length(this); /// <summary> /// The squared magnitude (length) of the <see cref="Vector3D"/>. /// </summary> public float MagnitudeSquared => LengthSquared(this); /// <summary> /// The normalized form of the <see cref="Vector3D"/> (a <see cref="Vector3D"/> with the same direction and a magnitude of 1). /// </summary> public Vector3D Normalized => Normalize(this); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing upwards. /// </summary> public readonly static Vector3D Up = new(0f, 1f, 0f); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing downwards. /// </summary> public readonly static Vector3D Down = new(0f, -1f, 0f); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing leftwards. /// </summary> public readonly static Vector3D Left = new(-1f, 0f, 0f); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing rightwards. /// </summary> public readonly static Vector3D Right = new(1f, 0f, 0f); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing forwards. /// </summary> public readonly static Vector3D Forward = new(0f, 0f, 1f); /// <summary> /// Represents the unit <see cref="Vector3D"/> pointing backwards. public readonly static Vector3D Backward = new(0f, 0f, -1f); /// <summary> /// Represents the zero <see cref="Vector3D"/>. /// </summary> public readonly static Vector3D Zero = new(0f, 0f, 0f); /// <summary> /// Represents the <see cref="Vector3D"/> with both components equal to 1. /// </summary> public readonly static Vector3D One = new(1f, 1f, 1f); public static Vector3D operator -(Vector3D vector) => new(0f - vector.X, 0f - vector.Y, 0f - vector.Z); public static Vector3D operator +(Vector3D left, Vector3D right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z); public static Vector3D operator -(Vector3D left, Vector3D right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z); public static Vector3D operator *(Vector3D vector, float value) => new(vector.X * value, vector.Y * value, vector.Z * value); public static Vector3D operator *(float value, Vector3D vector) => new(vector.X * value, vector.Y * value, vector.Z * value); public static Vector3D operator /(Vector3D vector, float value) => new(vector.X / value, vector.Y / value, vector.Z / value); public static bool operator ==(Vector3D left, Vector3D right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z; public static bool operator !=(Vector3D left, Vector3D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z; public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z); public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z); public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f); public static implicit operator Vector3D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f); /// <summary> /// Calculates the length of the <see cref="Vector3D"/>. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <returns>The length of the <see cref="Vector3D"/>.</returns> public static float Length(Vector3D vector) => Math.Sqrt(LengthSquared(vector)); /// <summary> /// Calculates the squared length of the <see cref="Vector3D"/>. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <returns>The squared length of the <see cref="Vector3D"/>.</returns> public static float LengthSquared(Vector3D vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z; /// <summary> /// Calculates the distance between two <see cref="Vector3D"/>s. /// </summary> /// <param name="from">The start <see cref="Vector3D"/>.</param> /// <param name="to">The end <see cref="Vector3D"/>.</param> /// <returns>The distance between the two <see cref="Vector3D"/>s.</returns> public static float Distance(Vector3D from, Vector3D to) => Length(FromTo(from, to)); /// <summary> /// Inverts the direction of the <see cref="Vector3D"/>. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <returns>The inverted <see cref="Vector3D"/>.</returns> public static Vector3D Invert(Vector3D vector) => -vector; /// <summary> /// Adds two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The sum of the two <see cref="Vector3D"/>s.</returns> public static Vector3D Add(Vector3D left, Vector3D right) => left + right; /// <summary> /// Subtracts one <see cref="Vector3D"/> from another. /// </summary> /// <param name="left">The <see cref="Vector3D"/> to subtract from.</param> /// <param name="right">The <see cref="Vector3D"/> to subtract.</param> /// <returns>The result of subtracting the second <see cref="Vector3D"/> from the first.</returns> public static Vector3D Subtract(Vector3D left, Vector3D right) => left - right; /// <summary> /// Multiplies a <see cref="Vector3D"/> by a scalar value. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <param name="value">The scalar value.</param> /// <returns>The result of multiplying the <see cref="Vector3D"/> by the scalar value.</returns> public static Vector3D Multiply(Vector3D vector, float value) => vector * value; /// <summary> /// Divides a <see cref="Vector3D"/> by a scalar value. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <param name="value">The scalar value.</param> /// <returns>The result of dividing the <see cref="Vector3D"/> by the scalar value.</returns> public static Vector3D Divide(Vector3D vector, float value) => vector / value; /// <summary> /// Calculates the absolute value of each component of the vector. /// </summary> /// <param name="vector">The <see cref="Vector3D"/>.</param> /// <returns>The <see cref="Vector3D"/> with each component's absolute value.</returns> public static Vector3D Abs(Vector3D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z)); /// <summary> /// Normalizes the <see cref="Vector3D"/> (creates a unit <see cref="Vector3D"/> with the same direction). /// </summary> /// <param name="vector">The <see cref="Vector3D"/> to normalize.</param> /// <returns>The normalized <see cref="Vector3D"/>.</returns> public static Vector3D Normalize(Vector3D vector) => vector / Length(vector); /// <summary> /// Reflects a <see cref="Vector3D"/> off a surface with the specified normal. /// </summary> /// <param name="vector">The incident <see cref="Vector3D"/>.</param> /// <param name="normal">The normal <see cref="Vector3D"/> of the surface.</param> /// <returns>The reflected <see cref="Vector3D"/>.</returns> public static Vector3D Reflect(Vector3D vector, Vector3D normal) => vector - 2f * Dot(vector, normal) * normal; /// <summary> /// Calculates the <see cref="Vector3D"/> from one point to another. /// </summary> /// <param name="from">The starting point.</param> /// <param name="to">The ending point.</param> /// <returns>The <see cref="Vector3D"/> from the starting point to the ending point.</returns> public static Vector3D FromTo(Vector3D from, Vector3D to) => to - from; /// <summary> /// Scales a <see cref="Vector3D"/> by another <see cref="Vector3D"/> component-wise. /// </summary> /// <param name="vector">The <see cref="Vector3D"/> to scale.</param> /// <param name="scale">The <see cref="Vector3D"/> containing the scaling factors for each component.</param> /// <returns>The scaled <see cref="Vector3D"/>.</returns> public static Vector3D Scale(Vector3D vector, Vector3D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z); /// <summary> /// Rotates a <see cref="Vector3D"/> around a normal by the specified angle (in radians). /// </summary> /// <param name="vector">The <see cref="Vector3D"/> to rotate.</param> /// <param name="normal">The <see cref="Vector3D"/> to rotate around.</param> /// <param name="angleInRadian">The angle to rotate by, in radians.</param> /// <returns>The rotated <see cref="Vector3D"/>.</returns> public static Vector3D Rotate(Vector3D vector, Vector3D normal, float angleInRadian) => vector * Math.Cos(angleInRadian) + Cross(normal, vector) * Math.Sin(angleInRadian) + normal * Dot(normal, vector) * (1f - Math.Cos(angleInRadian)); /// <summary> /// Returns the component-wise minimum of two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The <see cref="Vector3D"/> containing the minimum components from both input <see cref="Vector3D"/>s.</returns> public static Vector3D Min(Vector3D left, Vector3D 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); /// <summary> /// Returns the component-wise maximum of two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The <see cref="Vector3D"/> containing the maximum components from both input <see cref="Vector3D"/>s.</returns> public static Vector3D Max(Vector3D left, Vector3D 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); /// <summary> /// Clamps each component of a <see cref="Vector3D"/> between the corresponding component of two other <see cref="Vector3D"/>s. /// </summary> /// <param name="vector">The <see cref="Vector3D"/> to clamp.</param> /// <param name="min">The <see cref="Vector3D"/> representing the minimum values for each component.</param> /// <param name="max">The <see cref="Vector3D"/> representing the maximum values for each component.</param> /// <returns>A <see cref="Vector3D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector3D"/>s.</returns> public static Vector3D Clamp(Vector3D vector, Vector3D min, Vector3D 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)); /// <summary> /// Performs linear interpolation between two <see cref="Vector3D"/>s. /// </summary> /// <param name="from">The starting <see cref="Vector3D"/> (t = 0).</param> /// <param name="to">The ending <see cref="Vector3D"/> (t = 1).</param> /// <param name="t">The interpolation parameter.</param> /// <returns>The interpolated <see cref="Vector3D"/>.</returns> public static Vector3D Lerp(Vector3D from, Vector3D to, float t) => from + FromTo(from, to) * t; /// <summary> /// Calculates the cross product of two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The cross product of the two <see cref="Vector3D"/>s.</returns> public static Vector3D Cross(Vector3D left, Vector3D right) => new(left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X); /// <summary> /// Calculates the angle between two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The angle between the two <see cref="Vector3D"/>s in radians.</returns> public static float Angle(Vector3D left, Vector3D right) => Math.Acos(Dot(left, right) / (Length(left) * Length(right))); /// <summary> /// Calculates the dot product of two <see cref="Vector3D"/>s. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <returns>The dot product of the two <see cref="Vector3D"/>s.</returns> public static float Dot(Vector3D left, Vector3D right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z; /// <summary> /// Checks if two <see cref="Vector3D"/>s are approximately equal within a specified epsilon range. /// </summary> /// <param name="left">The first <see cref="Vector3D"/>.</param> /// <param name="right">The second <see cref="Vector3D"/>.</param> /// <param name="epsilon">The epsilon range.</param> /// <returns><see cref="true"/> if the <see cref="Vector3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns> public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon); /// <summary> /// Converts the <see cref="Vector3D"/> to its string representation. /// </summary> /// <returns>A string representation of the <see cref="Vector3D"/>.</returns> public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})"; /// <summary> /// Determines whether the specified object is equal to the current <see cref="Vector3D"/>. /// </summary> /// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param> /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns> public override bool Equals(object? obj) => obj is Vector3D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z); /// <summary> /// Generates a hash code for the <see cref="Vector3D"/>. /// </summary> /// <returns>A hash code for the <see cref="Vector3D"/>.</returns> public override int GetHashCode() => HashCode.Combine(X, Y, Z); } /// <summary> /// Provides extension methods for <see cref="Vector3D"/> type. /// </summary> public static class Vector3DExtensions { /// <inheritdoc cref="Vector3D.Length(Vector3D)" /> public static float Length(this Vector3D vector) => Vector3D.Length(vector); /// <inheritdoc cref="Vector3D.LengthSquared(Vector3D)" /> public static float LengthSquared(this Vector3D vector) => Vector3D.LengthSquared(vector); /// <inheritdoc cref="Vector3D.Distance(Vector3D, Vector3D)" /> public static float Distance(this Vector3D from, Vector3D to) => Vector3D.Distance(from, to); /// <inheritdoc cref="Vector3D.Invert(Vector3D)" /> public static Vector3D Invert(this Vector3D vector) => Vector3D.Invert(vector); /// <inheritdoc cref="Vector3D.Add(Vector3D, Vector3D)" /> public static Vector3D Add(this Vector3D vector, Vector3D vectorToAdd) => Vector3D.Add(vector, vectorToAdd); /// <inheritdoc cref="Vector3D.Subtract(Vector3D, Vector3D)" /> public static Vector3D Subtract(this Vector3D vector, Vector3D vectorToSubtract) => Vector3D.Subtract(vector, vectorToSubtract); /// <inheritdoc cref="Vector3D.Multiply(Vector3D, float)" /> public static Vector3D Multiply(this Vector3D vector, float value) => Vector3D.Multiply(vector, value); /// <inheritdoc cref="Vector3D.Divide(Vector3D, float)" /> public static Vector3D Divide(this Vector3D vector, float value) => Vector3D.Divide(vector, value); /// <inheritdoc cref="Vector3D.Abs(Vector3D)" /> public static Vector3D Abs(this Vector3D vector) => Vector3D.Abs(vector); /// <inheritdoc cref="Vector3D.Reflect(Vector3D, Vector3D)" /> public static Vector3D Reflect(this Vector3D vector, Vector3D normal) => Vector3D.Reflect(vector, normal); /// <inheritdoc cref="Vector3D.Normalize(Vector3D)" /> public static Vector3D Normalize(this Vector3D vector) => Vector3D.Normalize(vector); /// <inheritdoc cref="Vector3D.FromTo(Vector3D, Vector3D)" /> public static Vector3D FromTo(this Vector3D from, Vector3D to) => Vector3D.FromTo(from, to); /// <inheritdoc cref="Vector3D.Scale(Vector3D, Vector3D)" /> public static Vector3D Scale(this Vector3D vector, Vector3D scale) => Vector3D.Scale(vector, scale); /// <inheritdoc cref="Vector3D.Rotate(Vector3D, Vector3D, float)" /> public static Vector3D Rotate(this Vector3D vector, Vector3D normal, float angleInRadian) => Vector3D.Rotate(vector, normal, angleInRadian); /// <inheritdoc cref="Vector3D.Min(Vector3D, Vector3D)" /> public static Vector3D Min(this Vector3D left, Vector3D right) => Vector3D.Min(left, right); /// <inheritdoc cref="Vector3D.Max(Vector3D, Vector3D)" /> public static Vector3D Max(this Vector3D left, Vector3D right) => Vector3D.Max(left, right); /// <inheritdoc cref="Vector3D.Clamp(Vector3D, Vector3D, Vector3D)" /> public static Vector3D Clamp(this Vector3D vector, Vector3D min, Vector3D max) => Vector3D.Clamp(vector, min, max); /// <inheritdoc cref="Vector3D.Lerp(Vector3D, Vector3D, float)" /> public static Vector3D Lerp(this Vector3D from, Vector3D to, float t) => Vector3D.Lerp(from, to, t); /// <inheritdoc cref="Vector3D.Cross(Vector3D, Vector3D)" /> public static Vector3D Cross(this Vector3D left, Vector3D right) => Vector3D.Cross(left, right); /// <inheritdoc cref="Vector3D.Angle(Vector3D, Vector3D)" /> public static float AngleBetween(this Vector3D left, Vector3D right) => Vector3D.Angle(left, right); /// <inheritdoc cref="Vector3D.Dot(Vector3D, Vector3D)" /> public static float Dot(this Vector3D left, Vector3D right) => Vector3D.Dot(left, right); /// <inheritdoc cref="Vector3D.ApproximatelyEquals(Vector3D, Vector3D, float)" /> public static bool ApproximatelyEquals(this Vector3D left, Vector3D right, float epsilon = float.Epsilon) => Vector3D.ApproximatelyEquals(left, right, epsilon); }