diff --git a/Hypercube.Client/Graphics/Realisation/OpenGL/Texturing/TextureManager.cs b/Hypercube.Client/Graphics/Realisation/OpenGL/Texturing/TextureManager.cs index 6aef9ac..2d2c0fc 100644 --- a/Hypercube.Client/Graphics/Realisation/OpenGL/Texturing/TextureManager.cs +++ b/Hypercube.Client/Graphics/Realisation/OpenGL/Texturing/TextureManager.cs @@ -14,7 +14,7 @@ public sealed class TextureManager : ITextureManager { [Dependency] private readonly IResourceLoader _resourceLoader = default!; - public ITexture CreateBlank(Vector2Int size, Color color) + public ITexture CreateBlank(Vector2i size, Color color) { var data = new byte[size.X * size.Y * 4]; diff --git a/Hypercube.Client/Graphics/Viewports/Camera2D.cs b/Hypercube.Client/Graphics/Viewports/Camera2D.cs index 2163d73..086d7a5 100644 --- a/Hypercube.Client/Graphics/Viewports/Camera2D.cs +++ b/Hypercube.Client/Graphics/Viewports/Camera2D.cs @@ -13,14 +13,14 @@ public sealed class Camera2D : ICamera public Vector3 Position => _transform.Position; public Vector3 Rotation => _transform.Rotation.ToEuler(); public Vector3 Scale => _transform.Scale; - public Vector2Int Size { get; private set; } + public Vector2i Size { get; private set; } private readonly float _zFar; private readonly float _zNear; private Transform3 _transform = new(); - public Camera2D(Vector2Int size, Vector2 position, float zNear, float zFar) + public Camera2D(Vector2i size, Vector2 position, float zNear, float zFar) { Size = size; _zNear = zNear; diff --git a/Hypercube.Client/Graphics/Viewports/CameraManager.cs b/Hypercube.Client/Graphics/Viewports/CameraManager.cs index db15412..e202430 100644 --- a/Hypercube.Client/Graphics/Viewports/CameraManager.cs +++ b/Hypercube.Client/Graphics/Viewports/CameraManager.cs @@ -61,12 +61,12 @@ public void SetMainCamera(ICamera camera) MainCamera = camera; } - public ICamera CreateCamera2D(Vector2Int size) + public ICamera CreateCamera2D(Vector2i size) { return CreateCamera2D(size, Vector2.Zero); } - public ICamera CreateCamera2D(Vector2Int size, Vector2 position, float zNear = 0.1f, float zFar = 100f) + public ICamera CreateCamera2D(Vector2i size, Vector2 position, float zNear = 0.1f, float zFar = 100f) { return new Camera2D(size, position, zNear, zFar); } diff --git a/Hypercube.Client/Graphics/Viewports/ICamera.cs b/Hypercube.Client/Graphics/Viewports/ICamera.cs index 8da3707..2a9cbf1 100644 --- a/Hypercube.Client/Graphics/Viewports/ICamera.cs +++ b/Hypercube.Client/Graphics/Viewports/ICamera.cs @@ -11,7 +11,7 @@ public interface ICamera Vector3 Position { get; } Vector3 Rotation { get; } Vector3 Scale { get; } - Vector2Int Size { get; } + Vector2i Size { get; } void SetPosition(Vector3 position); void SetRotation(Vector3 rotation); diff --git a/Hypercube.Client/Graphics/Viewports/ICameraManager.cs b/Hypercube.Client/Graphics/Viewports/ICameraManager.cs index cd15d43..3b79ce4 100644 --- a/Hypercube.Client/Graphics/Viewports/ICameraManager.cs +++ b/Hypercube.Client/Graphics/Viewports/ICameraManager.cs @@ -12,6 +12,6 @@ public interface ICameraManager void SetMainCamera(ICamera camera); - ICamera CreateCamera2D(Vector2Int size); + ICamera CreateCamera2D(Vector2i size); void UpdateInput(ICamera? camera, float delta); } \ No newline at end of file diff --git a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Monitors.cs b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Monitors.cs index 91ea592..1d93252 100644 --- a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Monitors.cs +++ b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Monitors.cs @@ -82,7 +82,7 @@ public sealed class GlfwMonitorRegistration : MonitorHandle public readonly Monitor* Pointer; - public GlfwMonitorRegistration(MonitorId id, string name, Vector2Int size, int refreshRate, VideoMode[] videoModes, Monitor* pointer) : base(id, name, size, refreshRate, videoModes) + public GlfwMonitorRegistration(MonitorId id, string name, Vector2i size, int refreshRate, VideoMode[] videoModes, Monitor* pointer) : base(id, name, size, refreshRate, videoModes) { Pointer = pointer; } diff --git a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Window.cs b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Window.cs index 8ab8b0c..696d686 100644 --- a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Window.cs +++ b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Window.cs @@ -206,7 +206,7 @@ public void WindowSetTitle(WindowHandle window, string title) public void WindowSetMonitor(WindowHandle window, MonitorHandle registration) { - WindowSetMonitor(window, registration, Vector2Int.Zero); + WindowSetMonitor(window, registration, Vector2i.Zero); } public void WindowRequestAttention(WindowHandle window) @@ -217,7 +217,7 @@ public void WindowRequestAttention(WindowHandle window) OpenTK.Windowing.GraphicsLibraryFramework.GLFW.RequestWindowAttention(glfwWindow); } - public void WindowSetSize(WindowHandle window, Vector2Int size) + public void WindowSetSize(WindowHandle window, Vector2i size) { if (window is not GlfwWindowHandle glfwWindow) return; @@ -247,7 +247,7 @@ public void WindowSetOpacity(WindowHandle window, float opacity) OpenTK.Windowing.GraphicsLibraryFramework.GLFW.SetWindowOpacity(glfwWindow, opacity); } - public void WindowSetPosition(WindowHandle window, Vector2Int position) + public void WindowSetPosition(WindowHandle window, Vector2i position) { if (window is not GlfwWindowHandle glfwWindow) return; diff --git a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowing.cs b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowing.cs index a3cc258..c1f5977 100644 --- a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowing.cs +++ b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowing.cs @@ -95,7 +95,7 @@ public nint GetProcAddress(string procName) return OpenTK.Windowing.GraphicsLibraryFramework.GLFW.GetProcAddress(procName); } - public void WindowSetMonitor(WindowHandle window, MonitorHandle monitor, Vector2Int vector) + public void WindowSetMonitor(WindowHandle window, MonitorHandle monitor, Vector2i vector) { if (monitor is not GlfwMonitorRegistration glfwMonitor) return; diff --git a/Hypercube.Client/Hypercube.Client.csproj b/Hypercube.Client/Hypercube.Client.csproj index eb18085..c62b34e 100644 --- a/Hypercube.Client/Hypercube.Client.csproj +++ b/Hypercube.Client/Hypercube.Client.csproj @@ -18,6 +18,7 @@ + diff --git a/Hypercube.Graphics/Monitors/MonitorHandle.cs b/Hypercube.Graphics/Monitors/MonitorHandle.cs index e815926..2fa8bff 100644 --- a/Hypercube.Graphics/Monitors/MonitorHandle.cs +++ b/Hypercube.Graphics/Monitors/MonitorHandle.cs @@ -8,11 +8,11 @@ public class MonitorHandle { public MonitorId Id { get; } public string Name { get; } - public Vector2Int Size { get; } + public Vector2i Size { get; } public int RefreshRate { get; } public VideoMode[] VideoModes { get; } - public MonitorHandle(MonitorId id, string name, Vector2Int size, int refreshRate, VideoMode[] videoModes) + public MonitorHandle(MonitorId id, string name, Vector2i size, int refreshRate, VideoMode[] videoModes) { Id = id; Name = name; @@ -25,7 +25,7 @@ public MonitorHandle(MonitorId id, string name, int width, int height, int refre { Id = id; Name = name; - Size = new Vector2Int(width, height); + Size = new Vector2i(width, height); RefreshRate = refreshRate; VideoModes = videoModes; } diff --git a/Hypercube.Graphics/Shaders/IShaderProgram.cs b/Hypercube.Graphics/Shaders/IShaderProgram.cs index 8022afc..b6ff6ce 100644 --- a/Hypercube.Graphics/Shaders/IShaderProgram.cs +++ b/Hypercube.Graphics/Shaders/IShaderProgram.cs @@ -27,7 +27,7 @@ public interface IShaderProgram : IDisposable void SetUniform(string name, double value); void SetUniform(string name, Vector2 value); - void SetUniform(string name, Vector2Int value); + void SetUniform(string name, Vector2i value); void SetUniform(string name, Vector3 value); void SetUniform(string name, Matrix3X3 value, bool transpose = false); diff --git a/Hypercube.Graphics/Texturing/ITextureManager.cs b/Hypercube.Graphics/Texturing/ITextureManager.cs index 35f5b3a..4e6557c 100644 --- a/Hypercube.Graphics/Texturing/ITextureManager.cs +++ b/Hypercube.Graphics/Texturing/ITextureManager.cs @@ -9,7 +9,7 @@ namespace Hypercube.Graphics.Texturing; [PublicAPI] public interface ITextureManager { - ITexture CreateBlank(Vector2Int size, Color color); + ITexture CreateBlank(Vector2i size, Color color); ITexture CreateTexture(ResourcePath path); ITextureHandle CreateTextureHandle(ITexture texture); diff --git a/Hypercube.Graphics/Texturing/Texture.cs b/Hypercube.Graphics/Texturing/Texture.cs index c23e58a..d101fc3 100644 --- a/Hypercube.Graphics/Texturing/Texture.cs +++ b/Hypercube.Graphics/Texturing/Texture.cs @@ -11,7 +11,7 @@ namespace Hypercube.Graphics.Texturing; public const int PixelPerUnit = 32; public ResourcePath Path { get; } - public Vector2Int Size { get; } + public Vector2i Size { get; } public byte[] Data { get; } @@ -27,7 +27,7 @@ public Box2 Quad } } - public Texture(ResourcePath path, Vector2Int size, byte[] data) + public Texture(ResourcePath path, Vector2i size, byte[] data) { Path = path; Size = size; diff --git a/Hypercube.Graphics/Windowing/IWindowing.cs b/Hypercube.Graphics/Windowing/IWindowing.cs index fdb8312..bd258b0 100644 --- a/Hypercube.Graphics/Windowing/IWindowing.cs +++ b/Hypercube.Graphics/Windowing/IWindowing.cs @@ -33,12 +33,12 @@ public interface IWindowing : IDisposable void WindowDestroy(WindowHandle window); void WindowSetTitle(WindowHandle window, string title); void WindowSetMonitor(WindowHandle window, MonitorHandle monitor); - void WindowSetMonitor(WindowHandle window, MonitorHandle monitor, Vector2Int vector2Int); + void WindowSetMonitor(WindowHandle window, MonitorHandle monitor, Vector2i vector2I); void WindowRequestAttention(WindowHandle window); void WindowSetOpacity(WindowHandle window, float opacity); void WindowSetVisible(WindowHandle window, bool visible); - void WindowSetSize(WindowHandle window, Vector2Int size); - void WindowSetPosition(WindowHandle window, Vector2Int position); + void WindowSetSize(WindowHandle window, Vector2i size); + void WindowSetPosition(WindowHandle window, Vector2i position); void WindowSwapBuffers(WindowHandle window); void WindowSetIcons(WindowHandle window, List images); diff --git a/Hypercube.Graphics/Windowing/WindowCreateSettings.cs b/Hypercube.Graphics/Windowing/WindowCreateSettings.cs index 22861e2..4e10419 100644 --- a/Hypercube.Graphics/Windowing/WindowCreateSettings.cs +++ b/Hypercube.Graphics/Windowing/WindowCreateSettings.cs @@ -12,7 +12,7 @@ public sealed class WindowCreateSettings public int Height => Size.Y; public string Title { get; init; } = "Hypercube Window"; - public Vector2Int Size { get; init; } = new(1280, 720); + public Vector2i Size { get; init; } = new(1280, 720); public ITexture[]? WindowImages { get; init; } public MonitorHandle? Monitor { get; init; } diff --git a/Hypercube.Graphics/Windowing/WindowHandle.cs b/Hypercube.Graphics/Windowing/WindowHandle.cs index 9a230fc..ca1e5f1 100644 --- a/Hypercube.Graphics/Windowing/WindowHandle.cs +++ b/Hypercube.Graphics/Windowing/WindowHandle.cs @@ -13,8 +13,8 @@ public abstract class WindowHandle public bool DisposeOnClose { get; } public bool IsDisposed { get; } public float Ratio { get; init; } - public Vector2Int Size { get; set; } - public Vector2Int FramebufferSize { get; init; } + public Vector2i Size { get; set; } + public Vector2i FramebufferSize { get; init; } protected WindowHandle(WindowId id, nint pointer) { @@ -22,14 +22,14 @@ protected WindowHandle(WindowId id, nint pointer) Pointer = pointer; } - public void SetSize(Vector2Int size) + public void SetSize(Vector2i size) { Size = size; } public void SetSize(int width, int height) { - Size = new Vector2Int(width, height); + Size = new Vector2i(width, height); } public override string ToString() diff --git a/Hypercube.ImGui/IImGuiController.cs b/Hypercube.ImGui/IImGuiController.cs index 01feeb5..6282c32 100644 --- a/Hypercube.ImGui/IImGuiController.cs +++ b/Hypercube.ImGui/IImGuiController.cs @@ -14,7 +14,7 @@ public interface IImGuiController : IImGui void Render(); void InputFrame(); - void UpdateMousePosition(Vector2Int position); + void UpdateMousePosition(Vector2i position); void UpdateKey(Key key, KeyState state, KeyModifiers modifiers); void UpdateMouseButtons(MouseButton button, KeyState state, KeyModifiers modifiers); void UpdateMouseScroll(Vector2 offset); diff --git a/Hypercube.ImGui/Implementations/OpenGLImGuiController.Input.cs b/Hypercube.ImGui/Implementations/OpenGLImGuiController.Input.cs index c88bf67..331032a 100644 --- a/Hypercube.ImGui/Implementations/OpenGLImGuiController.Input.cs +++ b/Hypercube.ImGui/Implementations/OpenGLImGuiController.Input.cs @@ -14,7 +14,7 @@ public void InputFrame() { } - public void UpdateMousePosition(Vector2Int position) + public void UpdateMousePosition(Vector2i position) { _io.MousePos = position; } diff --git a/Hypercube.ImGui/Implementations/OpenGLImGuiController.Render.cs b/Hypercube.ImGui/Implementations/OpenGLImGuiController.Render.cs index b4024b3..1281b2c 100644 --- a/Hypercube.ImGui/Implementations/OpenGLImGuiController.Render.cs +++ b/Hypercube.ImGui/Implementations/OpenGLImGuiController.Render.cs @@ -25,7 +25,7 @@ private void Render(ImDrawDataPtr data) if (data.CmdListsCount == 0) return; - var frameBufferSize = new Vector2Int( + var frameBufferSize = new Vector2i( data.DisplaySize.X * data.FramebufferScale.X, data.DisplaySize.Y * data.FramebufferScale.Y); @@ -128,7 +128,7 @@ private void Render(ImDrawDataPtr data) GL.Disable(EnableCap.ScissorTest); } - private void SetupRender(ImDrawDataPtr data, Vector2Int frameBufferSize) + private void SetupRender(ImDrawDataPtr data, Vector2i frameBufferSize) { GL.Enable(EnableCap.Blend); GL.Enable(EnableCap.ScissorTest); diff --git a/Hypercube.Mathematics/Angle.cs b/Hypercube.Mathematics/Angle.cs index 9bcad14..d02940c 100644 --- a/Hypercube.Mathematics/Angle.cs +++ b/Hypercube.Mathematics/Angle.cs @@ -1,9 +1,12 @@ using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Hypercube.Mathematics.Extensions; using Hypercube.Mathematics.Vectors; +using JetBrains.Annotations; namespace Hypercube.Mathematics; +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential)] public readonly struct Angle : IEquatable, IEquatable { public static readonly Angle Zero = new(0); @@ -19,7 +22,7 @@ public double Degrees public Vector2 Vector { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new((float)System.Math.Cos(Theta), (float)System.Math.Sin(Theta)); + get => new(MathF.Cos((float) Theta), MathF.Sin((float) Theta)); } public Angle(double theta) @@ -27,10 +30,9 @@ public Angle(double theta) Theta = theta; } - public Angle(Vector2 vector2) + public Angle(Vector2 value) { - vector2 = vector2.Normalized; - Theta = System.Math.Atan2(vector2.X, vector2.Y); + Theta = value.Angle; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Hypercube.Mathematics/HyperMath.cs b/Hypercube.Mathematics/HyperMath.cs index 6b1c16a..685f4d7 100644 --- a/Hypercube.Mathematics/HyperMath.cs +++ b/Hypercube.Mathematics/HyperMath.cs @@ -2,7 +2,7 @@ public static class HyperMath { - public const double PI = System.Math.PI; + public const double PI = Math.PI; public const double PIOver2 = PI / 2; public const double PIOver4 = PI / 4; @@ -13,4 +13,11 @@ public static class HyperMath public const double RadiansToDegrees = 180 / PI; public const double DegreesToRadians = PI / 180; + + public static int MoveTowards(int current, int target, int distance) + { + return current < target ? + Math.Min(current + distance, target) : + Math.Max(current - distance, target); + } } \ No newline at end of file diff --git a/Hypercube.Mathematics/HyperMathF.cs b/Hypercube.Mathematics/HyperMathF.cs index 31aaa63..ffc547c 100644 --- a/Hypercube.Mathematics/HyperMathF.cs +++ b/Hypercube.Mathematics/HyperMathF.cs @@ -13,4 +13,11 @@ public static class HyperMathF public const float RadiansToDegrees = 180 / PI; public const float DegreesToRadians = PI / 180; + + public static float MoveTowards(float current, float target, float distance) + { + return current < target ? + MathF.Min(current + distance, target) : + MathF.Max(current - distance, target); + } } \ No newline at end of file diff --git a/Hypercube.Mathematics/Matrices/Matrix4X4.cs b/Hypercube.Mathematics/Matrices/Matrix4X4.cs index 2281851..687c290 100644 --- a/Hypercube.Mathematics/Matrices/Matrix4X4.cs +++ b/Hypercube.Mathematics/Matrices/Matrix4X4.cs @@ -320,38 +320,33 @@ public override string ToString() public static Matrix4X4 operator *(Matrix4X4 a, Matrix4X4 b) { - var result = Zero; - - result.M00 = (a.Row0 * b.Column0).Sum(); - result.M01 = (a.Row0 * b.Column1).Sum(); - result.M02 = (a.Row0 * b.Column2).Sum(); - result.M03 = (a.Row0 * b.Column3).Sum(); - - result.M10 = (a.Row1 * b.Column0).Sum(); - result.M11 = (a.Row1 * b.Column1).Sum(); - result.M12 = (a.Row1 * b.Column2).Sum(); - result.M13 = (a.Row1 * b.Column3).Sum(); - - result.M20 = (a.Row2 * b.Column0).Sum(); - result.M21 = (a.Row2 * b.Column1).Sum(); - result.M22 = (a.Row2 * b.Column2).Sum(); - result.M23 = (a.Row2 * b.Column3).Sum(); - - result.M30 = (a.Row3 * b.Column0).Sum(); - result.M31 = (a.Row3 * b.Column1).Sum(); - result.M32 = (a.Row3 * b.Column2).Sum(); - result.M33 = (a.Row3 * b.Column3).Sum(); - - return result; + return new Matrix4X4( + (a.Row0 * b.Column0).Summation, + (a.Row0 * b.Column1).Summation, + (a.Row0 * b.Column2).Summation, + (a.Row0 * b.Column3).Summation, + (a.Row1 * b.Column0).Summation, + (a.Row1 * b.Column1).Summation, + (a.Row1 * b.Column2).Summation, + (a.Row1 * b.Column3).Summation, + (a.Row2 * b.Column0).Summation, + (a.Row2 * b.Column1).Summation, + (a.Row2 * b.Column2).Summation, + (a.Row2 * b.Column3).Summation, + (a.Row3 * b.Column0).Summation, + (a.Row3 * b.Column1).Summation, + (a.Row3 * b.Column2).Summation, + (a.Row3 * b.Column3).Summation + ); } public static Vector4 operator *(Matrix4X4 a, Vector4 b) { return new Vector4( - (a.Row0 * b).Sum(), - (a.Row1 * b).Sum(), - (a.Row2 * b).Sum(), - (a.Row3 * b).Sum()); + (a.Row0 * b).Summation, + (a.Row1 * b).Summation, + (a.Row2 * b).Summation, + (a.Row3 * b).Summation); } public static bool operator ==(Matrix4X4 a, Matrix4X4 b) diff --git a/Hypercube.Mathematics/Vectors/Vector2.Compatibility.cs b/Hypercube.Mathematics/Vectors/Vector2.Compatibility.cs index 56ced34..4fdfd63 100644 --- a/Hypercube.Mathematics/Vectors/Vector2.Compatibility.cs +++ b/Hypercube.Mathematics/Vectors/Vector2.Compatibility.cs @@ -9,9 +9,9 @@ public readonly partial struct Vector2 */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(Vector2 vector) + public static implicit operator Vector2i(Vector2 vector) { - return new Vector2Int((int)vector.X, (int)vector.Y); + return new Vector2i((int)vector.X, (int)vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Hypercube.Mathematics/Vectors/Vector2.cs b/Hypercube.Mathematics/Vectors/Vector2.cs index 5201392..bb92c8f 100644 --- a/Hypercube.Mathematics/Vectors/Vector2.cs +++ b/Hypercube.Mathematics/Vectors/Vector2.cs @@ -1,41 +1,105 @@ -using System.Runtime.CompilerServices; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Hypercube.Mathematics.Extensions; using JetBrains.Annotations; namespace Hypercube.Mathematics.Vectors; -[PublicAPI, StructLayout(LayoutKind.Sequential)] -public readonly partial struct Vector2 : IEquatable, IComparable +/// +/// Represents a vector with two single-precision floating-point values. +/// +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential), DebuggerDisplay("({X}, {Y})")] +public readonly partial struct Vector2 : IEquatable, IComparable, IComparable, IEnumerable, ISpanFormattable { - public static readonly Vector2 NaN = new(float.NaN, float.NaN); - public static readonly Vector2 Zero = new(0, 0); - public static readonly Vector2 One = new(1, 1); - + /// + /// Vector (float.NaN, float.NaN). + /// + public static readonly Vector2 NaN = new(float.NaN); + + /// + /// Vector (float.PositiveInfinity, float.PositiveInfinity). + /// + public static readonly Vector2 PositiveInfinity = new(float.PositiveInfinity); + + /// + /// Vector (float.NegativeInfinity, float.NegativeInfinity). + /// + public static readonly Vector2 NegativeInfinity = new(float.NegativeInfinity); + + /// + /// Vector (0, 0). + /// + public static readonly Vector2 Zero = new(0); + + /// + /// Vector (1, 1). + /// + public static readonly Vector2 One = new(1); + + /// + /// Vector (1, 0). + /// public static readonly Vector2 UnitX = new(1, 0); + + /// + /// Vector (0, 1). + /// public static readonly Vector2 UnitY = new(0, 1); + /// + /// Vector X component. + /// public readonly float X; + + /// + /// Vector Y component. + /// public readonly float Y; + /// + /// Returns the ratio of X to Y. + /// public float AspectRatio { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => X / Y; } + /// + /// Gets the square of the vector length (magnitude). + /// + /// + /// Allows you to avoid using the rather expensive sqrt operation. + /// (On ARM64 hardware may use the FRSQRTE instruction, which would take away this advantage). + /// + /// public float LengthSquared { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => X * X + Y * Y; } + /// + /// Gets the length (magnitude) of the vector. + /// + /// + /// On ARM64 hardware this may use the FRSQRTE instruction + /// which performs a single Newton-Raphson iteration. + /// On hardware without specialized support + /// sqrt is used, which makes the method less fast. + /// + /// public float Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => MathF.Sqrt(LengthSquared); + get => 1f / MathF.ReciprocalSqrtEstimate(LengthSquared); } + /// + /// Copy of scaled to unit length. + /// public Vector2 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -47,7 +111,39 @@ public float Angle [MethodImpl(MethodImplOptions.AggressiveInlining)] get => MathF.Atan2(Y, X); } + + /// + /// Summation of all vector components. + /// + public float Summation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X + Y; + } + + /// + /// Production of all vector components. + /// + public float Production + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * Y; + } + public float this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return index switch + { + 0 => X, + 1 => Y, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + public Vector2(float x, float y) { X = x; @@ -90,41 +186,131 @@ public Vector2 WithY(float value) return new Vector2(X, value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float DistanceSquared(Vector2 value) + { + return DistanceSquared(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Distance(Vector2 value) + { + return Distance(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Dot(Vector2 value) + { + return Dot(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Reflect(Vector2 normal) + { + return Reflect(this, normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 MoveTowards(Vector2 target, float distance) + { + return MoveTowards(this, target, distance); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 Rotate(float angle) { var cos = MathF.Cos(angle); var sin = MathF.Sin(angle); - return new Vector2( cos * X - sin * Y, sin * X + cos * Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float DistanceSquared(Vector2 other) + public Vector2 Clamp(Vector2 min, Vector2 max) { - return (this - other).LengthSquared; + return Clamp(this, min, max); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Distance(Vector2 other) + public Vector2 Clamp(float min, float max) { - return MathF.Sqrt(DistanceSquared(this, other)); + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Lerp(Vector2 value, float amount) + { + return Lerp(this, value, amount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Max(Vector2 value) + { + return Max(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Min(Vector2 value) + { + return Min(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Abs() + { + return Abs(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Round() + { + return Round(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Round(int digits) + { + return Round(this, digits); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Ceiling() + { + return Ceiling(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Floor() + { + return Floor(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Vector2 other) + { + return LengthSquared.CompareTo(other.LengthSquared); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Dot(Vector2 other) + public int CompareTo(float other) { - return Dot(this, other); + return LengthSquared.CompareTo(other * other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Cross(Vector2 other) + public IEnumerator GetEnumerator() { - return Cross(this, other); + yield return X; + yield return Y; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector2 other) { @@ -143,29 +329,41 @@ public override int GetHashCode() { return HashCode.Combine(X, Y); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return $"{X}, {Y}"; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ToString(string? format, IFormatProvider? formatProvider) + { + return $"{X}, {Y}".ToString(formatProvider); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator +(Vector2 a, Vector2 b) + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return destination.TryWrite(provider, $"{X}, {Y}", out charsWritten); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 operator +(Vector2 a, Vector2 valueB) { - return new Vector2(a.X + b.X, a.Y + b.Y); + return new Vector2(a.X + valueB.X, a.Y + valueB.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator +(float a, Vector2 b) + public static Vector2 operator +(float a, Vector2 valueB) { - return new Vector2(b.X + a, b.Y + a); + return new Vector2(valueB.X + a, valueB.Y + a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator +(Vector2 a, float b) + public static Vector2 operator +(Vector2 a, float valueB) { - return new Vector2(a.X + b, a.Y + b); + return new Vector2(a.X + valueB, a.Y + valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -175,120 +373,236 @@ public override string ToString() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator -(Vector2 a, Vector2 b) + public static Vector2 operator -(Vector2 a, Vector2 valueB) { - return new Vector2(a.X - b.X, a.Y - b.Y); + return new Vector2(a.X - valueB.X, a.Y - valueB.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator -(float a, Vector2 b) + public static Vector2 operator -(float a, Vector2 valueB) { - return new Vector2(b.X - a, b.Y - a); + return new Vector2(valueB.X - a, valueB.Y - a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator -(Vector2 a, float b) + public static Vector2 operator -(Vector2 a, float valueB) { - return new Vector2(a.X - b, a.Y - b); + return new Vector2(a.X - valueB, a.Y - valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(Vector2 a, Vector2 b) + public static Vector2 operator *(Vector2 a, Vector2 valueB) { - return new Vector2(a.X * b.X, a.Y * b.Y); + return new Vector2(a.X * valueB.X, a.Y * valueB.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(float a, Vector2 b) + public static Vector2 operator *(float a, Vector2 valueB) { - return new Vector2(b.X * a, b.Y * a); + return new Vector2(valueB.X * a, valueB.Y * a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(Vector2 a, float b) + public static Vector2 operator *(Vector2 a, float valueB) { - return new Vector2(a.X * b, a.Y * b); + return new Vector2(a.X * valueB, a.Y * valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator /(Vector2 a, Vector2 b) + public static Vector2 operator /(Vector2 a, Vector2 valueB) { - return new Vector2(a.X / b.X, a.Y / b.Y); + return new Vector2(a.X / valueB.X, a.Y / valueB.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator /(float a, Vector2 b) + public static Vector2 operator /(float a, Vector2 valueB) { - return new Vector2(b.X / a, b.Y / a); + return new Vector2(valueB.X / a, valueB.Y / a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator /(Vector2 a, float b) + public static Vector2 operator /(Vector2 a, float valueB) { - return new Vector2(a.X / b, a.Y / b); + return new Vector2(a.X / valueB, a.Y / valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Vector2 a, Vector2 b) + public static bool operator ==(Vector2 a, Vector2 valueB) { - return a.Equals(b); + return a.Equals(valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector2 a, Vector2 b) + public static bool operator !=(Vector2 a, Vector2 valueB) { - return !a.Equals(b); + return !a.Equals(valueB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistanceSquared(Vector2 a, Vector2 b) + public static bool operator <(Vector2 valueA, Vector2 valueB) { - return (a - b).LengthSquared; + return valueA.CompareTo(valueB) == -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Vector2 valueA, Vector2 valueB) + { + return valueA.CompareTo(valueB) == 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Distance(Vector2 a, Vector2 b) + public static bool operator <=(Vector2 valueA, Vector2 valueB) + { + return valueA.CompareTo(valueB) is -1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Vector2 valueA, Vector2 valueB) + { + return valueA.CompareTo(valueB) is 1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Vector2 valueA, int valueB) { - return MathF.Sqrt(DistanceSquared(a, b)); + return valueA.CompareTo(valueB) == -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Vector2 valueA, int valueB) + { + return valueA.CompareTo(valueB) == 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Max(Vector2 a, Vector2 b) + public static bool operator <=(Vector2 valueA, int valueB) { - return new Vector2( - MathF.Max(a.X, b.X), - MathF.Max(a.Y, b.Y)); + return valueA.CompareTo(valueB) is -1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Vector2 valueA, int valueB) + { + return valueA.CompareTo(valueB) is 1 or 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Min(Vector2 a, Vector2 b) + public static float DistanceSquared(Vector2 valueA, Vector2 valueB) { - return new Vector2( - MathF.Min(a.X, b.X), - MathF.Min(a.Y, b.Y)); + return (valueA - valueB).LengthSquared; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(Vector2 a, Vector2 b) + public static float Distance(Vector2 valueA, Vector2 valueB) { - return a.X * b.X + a.Y * b.Y; + return (valueA - valueB).Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector2 valueA, Vector2 valueB) + { + return valueA.X * valueB.X + valueA.Y * valueB.Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Cross(Vector2 a, Vector2 b) + public static Vector2 Reflect(Vector2 value, Vector2 normal) { - return a.X * b.Y - a.Y * b.X; + return value - 2.0f * (Dot(value, normal) * normal); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(Vector2 other) + public static Vector2 MoveTowards(Vector2 current, Vector2 target, float distance) { - return LengthSquared.CompareTo(other.LengthSquared); + return new Vector2( + HyperMathF.MoveTowards(current.X, target.X, distance), + HyperMathF.MoveTowards(current.Y, target.Y, distance)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Lerp(Vector2 vectorA, Vector2 vectorB, float amount) + { + return new Vector2( + float.Lerp(vectorA.X, vectorB.X, amount), + float.Lerp(vectorA.Y, vectorB.Y, amount)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(Vector2 other, Func selector) + public static Vector2 Clamp(Vector2 value, Vector2 min, Vector2 max) { - return selector(this).CompareTo(selector(other)); + return new Vector2( + float.Clamp(value.X, min.X, max.X), + float.Clamp(value.Y, min.Y, max.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Clamp(Vector2 value, float min, float max) + { + return new Vector2( + float.Clamp(value.X, min, max), + float.Clamp(value.Y, min, max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Max(Vector2 valueA, Vector2 valueB) + { + return new Vector2( + MathF.Max(valueA.X, valueB.X), + MathF.Max(valueA.Y, valueB.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Min(Vector2 valueA, Vector2 valueB) + { + return new Vector2( + MathF.Min(valueA.X, valueB.X), + MathF.Min(valueA.Y, valueB.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Abs(Vector2 value) + { + return new Vector2( + Math.Abs(value.X), + Math.Abs(value.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Round(Vector2 value) + { + return new Vector2( + Math.Round(value.X), + Math.Round(value.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Round(Vector2 value, int digits) + { + return new Vector2( + Math.Round(value.X, digits), + Math.Round(value.Y, digits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Ceiling(Vector2 value) + { + return new Vector2( + Math.Ceiling(value.X), + Math.Ceiling(value.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Floor(Vector2 value) + { + return new Vector2( + Math.Floor(value.X), + Math.Floor(value.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Sign(Vector2 value) + { + return new Vector2( + Math.Sign(value.X), + Math.Sign(value.Y)); } } \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector2Int.cs b/Hypercube.Mathematics/Vectors/Vector2Int.cs deleted file mode 100644 index ab310c6..0000000 --- a/Hypercube.Mathematics/Vectors/Vector2Int.cs +++ /dev/null @@ -1,245 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using JetBrains.Annotations; - -namespace Hypercube.Mathematics.Vectors; - -[PublicAPI, StructLayout(LayoutKind.Sequential)] -public readonly partial struct Vector2Int : IEquatable, IComparable, IComparable -{ - public static readonly Vector2Int Zero = new(0, 0); - public static readonly Vector2Int One = new(1, 1); - - public static readonly Vector2Int UnitX = new(1, 0); - public static readonly Vector2Int UnitY = new(0, 1); - - public readonly int X; - public readonly int Y; - - public float AspectRatio - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => X / (float)Y; - } - - public float LengthSquared - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => X * X + Y * Y; - } - - public float Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => MathF.Sqrt(LengthSquared); - } - - public Vector2Int Normalized - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this / Length; - } - - public Vector2Int(int x, int y) - { - X = x; - Y = y; - } - - public Vector2Int(float x, float y) - { - X = (int) x; - Y = (int) y; - } - - public Vector2Int(int value) - { - X = value; - Y = value; - } - - public Vector2Int(float value) - { - X = (int) value; - Y = (int) value; - } - - public Vector2Int(Vector2Int vector2Int) : this(vector2Int.X, vector2Int.Y) - { - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2Int WithX(int value) - { - return new Vector2Int(value, Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2Int WithY(int value) - { - return new Vector2Int(X, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(int other) - { - return LengthSquared.CompareTo(other * other); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(Vector2Int other) - { - return LengthSquared.CompareTo(other.LengthSquared); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Vector2Int other) - { - return X == other.X && - Y == other.Y; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object? obj) - { - return obj is Vector2Int vector && Equals(vector); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return HashCode.Combine(X, Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() - { - return $"{X}, {Y}"; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator +(Vector2Int a, Vector2Int b) - { - return new Vector2Int(a.X + b.X, a.Y + b.Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator +(Vector2Int a, int b) - { - return new Vector2Int(a.X + b, a.Y + b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator -(Vector2Int a) - { - return new Vector2Int(-a.X, -a.Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator -(Vector2Int a, Vector2Int b) - { - return new Vector2Int(a.X - b.X, a.Y - b.Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator -(Vector2Int a, int b) - { - return new Vector2Int(a.X - b, a.Y - b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator *(Vector2Int a, Vector2Int b) - { - return new Vector2Int(a.X * b.X, a.Y * b.Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator *(Vector2Int a, int b) - { - return new Vector2Int(a.X * b, a.Y * b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(Vector2Int a, float b) - { - return new Vector2(a.X * b, a.Y * b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator /(Vector2Int a, Vector2Int b) - { - return new Vector2Int(a.X / b.X, a.Y / b.Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2Int operator /(Vector2Int a, int b) - { - return new Vector2Int(a.X / b, a.Y / b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator /(Vector2Int a, float b) - { - return new Vector2(a.X / b, a.Y / b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Vector2Int a, Vector2Int b) - { - return a.Equals(b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector2Int a, Vector2Int b) - { - return !a.Equals(b); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <(Vector2Int a, Vector2Int b) - { - return a.CompareTo(b) == -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >(Vector2Int a, Vector2Int b) - { - return a.CompareTo(b) == 1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <=(Vector2Int a, Vector2Int b) - { - return a.CompareTo(b) is -1 or 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >=(Vector2Int a, Vector2Int b) - { - return a.CompareTo(b) is 1 or 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <(Vector2Int a, int b) - { - return a.CompareTo(b) == -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >(Vector2Int a, int b) - { - return a.CompareTo(b) == 1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <=(Vector2Int a, int b) - { - return a.CompareTo(b) is -1 or 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >=(Vector2Int a, int b) - { - return a.CompareTo(b) is 1 or 0; - } -} \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector2Int.Compatibility.cs b/Hypercube.Mathematics/Vectors/Vector2i.Compatibility.cs similarity index 61% rename from Hypercube.Mathematics/Vectors/Vector2Int.Compatibility.cs rename to Hypercube.Mathematics/Vectors/Vector2i.Compatibility.cs index e61341d..1758147 100644 --- a/Hypercube.Mathematics/Vectors/Vector2Int.Compatibility.cs +++ b/Hypercube.Mathematics/Vectors/Vector2i.Compatibility.cs @@ -1,21 +1,23 @@ -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Hypercube.Mathematics.Vectors; -public readonly partial struct Vector2Int +[SuppressMessage("ReSharper", "InconsistentNaming")] +public readonly partial struct Vector2i { /* * Self Compatibility */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2(Vector2Int vector) + public static implicit operator Vector2(Vector2i vector) { return new Vector2(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector3(Vector2Int vector) + public static implicit operator Vector3(Vector2i vector) { return new Vector3(vector.X, vector.Y, 0f); } @@ -25,13 +27,13 @@ public static implicit operator Vector3(Vector2Int vector) */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int((int x, int y) a) + public static implicit operator Vector2i((int x, int y) a) { - return new Vector2Int(a.x, a.y); + return new Vector2i(a.x, a.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator (int x, int y)(Vector2Int a) + public static implicit operator (int x, int y)(Vector2i a) { return (a.X, a.Y); } @@ -41,13 +43,13 @@ public static implicit operator (int x, int y)(Vector2Int a) */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(System.Numerics.Vector2 vector) + public static implicit operator Vector2i(System.Numerics.Vector2 vector) { - return new Vector2Int((int)vector.X, (int)vector.Y); + return new Vector2i((int)vector.X, (int)vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator System.Numerics.Vector2(Vector2Int vector) + public static implicit operator System.Numerics.Vector2(Vector2i vector) { return new System.Numerics.Vector2(vector.X, vector.Y); } @@ -57,13 +59,13 @@ public static implicit operator System.Numerics.Vector2(Vector2Int vector) */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(System.Drawing.Size size) + public static implicit operator Vector2i(System.Drawing.Size size) { - return new Vector2Int(size.Width, size.Height); + return new Vector2i(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator System.Drawing.Size(Vector2Int vector2) + public static implicit operator System.Drawing.Size(Vector2i vector2) { return new System.Drawing.Size(vector2.X, vector2.Y); } @@ -73,13 +75,13 @@ public static implicit operator System.Drawing.Size(Vector2Int vector2) */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(OpenTK.Mathematics.Vector2i vector) + public static implicit operator Vector2i(OpenTK.Mathematics.Vector2i vector) { - return new Vector2Int(vector.X, vector.Y); + return new Vector2i(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator OpenTK.Mathematics.Vector2i(Vector2Int vector) + public static implicit operator OpenTK.Mathematics.Vector2i(Vector2i vector) { return new OpenTK.Mathematics.Vector2i(vector.X, vector.Y); } @@ -89,13 +91,13 @@ public static implicit operator OpenTK.Mathematics.Vector2i(Vector2Int vector) */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(OpenToolkit.Mathematics.Vector2i vector) + public static implicit operator Vector2i(OpenToolkit.Mathematics.Vector2i vector) { - return new Vector2Int(vector.X, vector.Y); + return new Vector2i(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator OpenToolkit.Mathematics.Vector2i(Vector2Int vector) + public static implicit operator OpenToolkit.Mathematics.Vector2i(Vector2i vector) { return new OpenToolkit.Mathematics.Vector2i(vector.X, vector.Y); } diff --git a/Hypercube.Mathematics/Vectors/Vector2i.cs b/Hypercube.Mathematics/Vectors/Vector2i.cs new file mode 100644 index 0000000..751c9cb --- /dev/null +++ b/Hypercube.Mathematics/Vectors/Vector2i.cs @@ -0,0 +1,455 @@ +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using JetBrains.Annotations; + +namespace Hypercube.Mathematics.Vectors; + +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential), DebuggerDisplay("({X}, {Y})")] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public readonly partial struct Vector2i : IEquatable, IComparable, IComparable, IEnumerable +{ + /// + /// Vector (0, 0). + /// + public static readonly Vector2i Zero = new(0); + + /// + /// Vector (1, 1). + /// + public static readonly Vector2i One = new(1); + + /// + /// Vector (1, 0). + /// + public static readonly Vector2i UnitX = new(1, 0); + + /// + /// Vector (0, 1). + /// + public static readonly Vector2i UnitY = new(0, 1); + + /// + /// Vector X component. + /// + public readonly int X; + + /// + /// Vector Y component. + /// + public readonly int Y; + + public float AspectRatio + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X / (float) Y; + } + + public float LengthSquared + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * X + Y * Y; + } + + public float Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => 1f / MathF.ReciprocalSqrtEstimate(LengthSquared); + } + + public Vector2i Normalized + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this / Length; + } + + public float Angle + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => MathF.Atan2(Y, X); + } + + public int Summation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X + Y; + } + + public int Production + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * Y; + } + + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return index switch + { + 0 => X, + 1 => Y, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + public Vector2i(int x, int y) + { + X = x; + Y = y; + } + + public Vector2i(int value) + { + X = value; + Y = value; + } + + public Vector2i(float x, float y) + { + X = (int) x; + Y = (int) y; + } + + public Vector2i(float value) + { + X = (int) value; + Y = (int) value; + } + + public Vector2i(Vector2i value) + { + X = value.X; + Y = value.Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i WithX(int value) + { + return new Vector2i(value, Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i WithY(int value) + { + return new Vector2i(X, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float DistanceSquared(Vector2i value) + { + return DistanceSquared(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Distance(Vector2i value) + { + return Distance(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Dot(Vector2i value) + { + return Dot(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Clamp(Vector2i min, Vector2i max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Clamp(int min, int max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Max(Vector2i value) + { + return Max(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Min(Vector2i value) + { + return Min(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Abs() + { + return Abs(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i MoveTowards(Vector2i target, int distance) + { + return MoveTowards(this, target, distance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2i Rotate(float angle) + { + var cos = MathF.Cos(angle); + var sin = MathF.Sin(angle); + return new Vector2i( + cos * X - sin * Y, + sin * X + cos * Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(int other) + { + return LengthSquared.CompareTo(other * other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Vector2i other) + { + return LengthSquared.CompareTo(other.LengthSquared); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerator GetEnumerator() + { + yield return X; + yield return Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Vector2i other) + { + return X == other.X && + Y == other.Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) + { + return obj is Vector2i vector && Equals(vector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return HashCode.Combine(X, Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() + { + return $"{X}, {Y}"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator +(Vector2i a, Vector2i b) + { + return new Vector2i(a.X + b.X, a.Y + b.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator +(Vector2i a, int b) + { + return new Vector2i(a.X + b, a.Y + b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator -(Vector2i a) + { + return new Vector2i(-a.X, -a.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator -(Vector2i a, Vector2i b) + { + return new Vector2i(a.X - b.X, a.Y - b.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator -(Vector2i a, int b) + { + return new Vector2i(a.X - b, a.Y - b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator *(Vector2i a, Vector2i b) + { + return new Vector2i(a.X * b.X, a.Y * b.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator *(Vector2i a, int b) + { + return new Vector2i(a.X * b, a.Y * b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator *(Vector2i a, float b) + { + return new Vector2i(a.X * b, a.Y * b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator /(Vector2i a, Vector2i b) + { + return new Vector2i(a.X / b.X, a.Y / b.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator /(Vector2i a, int b) + { + return new Vector2i(a.X / b, a.Y / b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i operator /(Vector2i a, float b) + { + return new Vector2i(a.X / b, a.Y / b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Vector2i a, Vector2i b) + { + return a.Equals(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Vector2i a, Vector2i b) + { + return !a.Equals(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Vector2i a, Vector2i b) + { + return a.CompareTo(b) == -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Vector2i a, Vector2i b) + { + return a.CompareTo(b) == 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Vector2i a, Vector2i b) + { + return a.CompareTo(b) is -1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Vector2i a, Vector2i b) + { + return a.CompareTo(b) is 1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Vector2i a, int b) + { + return a.CompareTo(b) == -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Vector2i a, int b) + { + return a.CompareTo(b) == 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Vector2i a, int b) + { + return a.CompareTo(b) is -1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Vector2i a, int b) + { + return a.CompareTo(b) is 1 or 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistanceSquared(Vector2i valueA, Vector2i valueB) + { + return (valueA - valueB).LengthSquared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Distance(Vector2i valueA, Vector2i valueB) + { + return (valueA - valueB).Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector2i valueA, Vector2i valueB) + { + return valueA.X * valueB.X + valueA.Y * valueB.Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i MoveTowards(Vector2i current, Vector2i target, int distance) + { + return new Vector2i( + HyperMathF.MoveTowards(current.X, target.X, distance), + HyperMathF.MoveTowards(current.Y, target.Y, distance)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Clamp(Vector2i value, Vector2i min, Vector2i max) + { + return new Vector2i( + int.Clamp(value.X, min.X, max.X), + int.Clamp(value.Y, min.Y, max.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Clamp(Vector2i value, int min, int max) + { + return new Vector2i( + int.Clamp(value.X, min, max), + int.Clamp(value.Y, min, max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Max(Vector2i valueA, Vector2i valueB) + { + return new Vector2i( + MathF.Max(valueA.X, valueB.X), + MathF.Max(valueA.Y, valueB.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Min(Vector2i valueA, Vector2i valueB) + { + return new Vector2i( + MathF.Min(valueA.X, valueB.X), + MathF.Min(valueA.Y, valueB.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Abs(Vector2i value) + { + return new Vector2i( + Math.Abs(value.X), + Math.Abs(value.Y)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2i Sign(Vector2i value) + { + return new Vector2i( + Math.Sign(value.X), + Math.Sign(value.Y)); + } +} \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector3.Compatibility.cs b/Hypercube.Mathematics/Vectors/Vector3.Compatibility.cs index 053a786..50247b3 100644 --- a/Hypercube.Mathematics/Vectors/Vector3.Compatibility.cs +++ b/Hypercube.Mathematics/Vectors/Vector3.Compatibility.cs @@ -15,9 +15,9 @@ public static implicit operator Vector2(Vector3 vector) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Vector2Int(Vector3 vector) + public static implicit operator Vector2i(Vector3 vector) { - return new Vector2Int((int)vector.X, (int)vector.Y); + return new Vector2i((int)vector.X, (int)vector.Y); } /* diff --git a/Hypercube.Mathematics/Vectors/Vector3.cs b/Hypercube.Mathematics/Vectors/Vector3.cs index 3847e5f..1498dd9 100644 --- a/Hypercube.Mathematics/Vectors/Vector3.cs +++ b/Hypercube.Mathematics/Vectors/Vector3.cs @@ -1,41 +1,151 @@ -using System.Runtime.CompilerServices; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Hypercube.Mathematics.Extensions; +using JetBrains.Annotations; namespace Hypercube.Mathematics.Vectors; -[StructLayout(LayoutKind.Sequential)] -public readonly partial struct Vector3 : IEquatable +/// +/// Represents a vector with three single-precision floating-point values. +/// +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential), DebuggerDisplay("({X}, {Y}, {Z})")] +public readonly partial struct Vector3 : IEquatable, IComparable, IComparable, IEnumerable { + /// + /// Vector (float.NaN, float.NaN, float.NaN). + /// + public static readonly Vector3 NaN = new(float.NaN); + + /// + /// Vector (float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity). + /// + public static readonly Vector3 PositiveInfinity = new(float.PositiveInfinity); + + /// + /// Vector (float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity). + /// + public static readonly Vector3 NegativeInfinity = new(float.NegativeInfinity); + + /// + /// Vector (0, 0, 0). + /// public static readonly Vector3 Zero = new(0, 0, 0); + + /// + /// Vector (1, 1, 1). + /// public static readonly Vector3 One = new(1, 1, 1); + /// + /// Vector (1, 0, 0). + /// public static readonly Vector3 UnitX = new(1, 0, 0); + + /// + /// Vector (0, 1, 0). + /// public static readonly Vector3 UnitY = new(0, 1, 0); + + /// + /// Vector (0, 0, 1). + /// public static readonly Vector3 UnitZ = new(0, 0, 1); + /// + /// Vector X component. + /// public readonly float X; + + /// + /// Vector Y component. + /// public readonly float Y; + + /// + /// Vector Z component. + /// public readonly float Z; - + + /// + /// Gets the square of the vector length (magnitude). + /// + /// + /// Allows you to avoid using the rather expensive sqrt operation. + /// (On ARM64 hardware may use the FRSQRTE instruction, which would take away this advantage). + /// + /// public float LengthSquared { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => X * X + Y * Y + Z * Z; } + /// + /// Gets the length (magnitude) of the vector. + /// + /// + /// On ARM64 hardware this may use the FRSQRTE instruction + /// which performs a single Newton-Raphson iteration. + /// On hardware without specialized support + /// sqrt is used, which makes the method less fast. + /// + /// public float Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => MathF.Sqrt(LengthSquared); + get => 1f / MathF.ReciprocalSqrtEstimate(LengthSquared); } + /// + /// Copy of scaled to unit length. + /// public Vector3 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this / Length; } + /// + /// Summation of all vector components. + /// + public float Summation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X + Y + Z; + } + + /// + /// Production of all vector components. + /// + public float Production + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * Y * Z; + } + + public Vector2 XY + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(X, Y); + } + + public float this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return index switch + { + 0 => X, + 1 => Y, + 2 => Z, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + public Vector3(float x, float y, float z) { X = x; @@ -43,26 +153,46 @@ public Vector3(float x, float y, float z) Z = z; } - public Vector3(float value) : this(value, value, value) + public Vector3(float value) { + X = value; + Y = value; + Z = value; } - public Vector3(Vector2 vector2) : this(vector2.X, vector2.Y, 0) + public Vector3(double x, double y, double z) { + X = (float) x; + Y = (float) y; + Z = (float) z; } - public Vector3(Vector2 vector2, float z) : this(vector2.X, vector2.Y, z) + public Vector3(double value) { + X = (float) value; + Y = (float) value; + Z = (float) value; } - - public Vector3(Vector3 vector3) : this(vector3.X, vector3.Y, vector3.Z) + + public Vector3(Vector2 value) { + X = value.X; + Y = value.Y; + Z = 0; + } + + public Vector3(Vector2 value, float z) + { + X = value.X; + Y = value.Y; + Z = z; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Sum() + public Vector3(Vector3 value) { - return X + Y + Z; + X = value.X; + Y = value.Y; + Z = value.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -76,17 +206,133 @@ public Vector3 WithY(float value) { return new Vector3(X, value, Z); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 WithZ(float value) { return new Vector3(X, Y, value); - } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float DistanceSquared(Vector3 value) + { + return DistanceSquared(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Distance(Vector3 value) + { + return Distance(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Cross(Vector3 value) + { + return Cross(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Dot(Vector3 value) + { + return Dot(this, value); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector3 Cross(Vector3 other) + public Vector3 Reflect(Vector3 normal) + { + return Reflect(this, normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 MoveTowards(Vector3 target, float distance) + { + return MoveTowards(this, target, distance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Clamp(Vector3 min, Vector3 max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Clamp(float min, float max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Lerp(Vector3 value, float amount) + { + return Lerp(this, value, amount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Max(Vector3 value) + { + return Max(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Min(Vector3 value) + { + return Min(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Abs() + { + return Abs(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Round() + { + return Round(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Round(int digits) + { + return Round(this, digits); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Ceiling() + { + return Ceiling(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 Floor() + { + return Floor(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Vector3 other) + { + return LengthSquared.CompareTo(other.LengthSquared); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(float other) { - return Vector3.Cross(this, other); + return LengthSquared.CompareTo(other * other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerator GetEnumerator() + { + yield return X; + yield return Y; + yield return Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -133,6 +379,12 @@ public override string ToString() return new Vector3(a.X + b, a.Y + b, a.Z + b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(float a, Vector3 b) + { + return new Vector3(a + b.X, a + b.Y, a + b.Z); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator -(Vector3 a) { @@ -157,6 +409,12 @@ public override string ToString() return new Vector3(a.X - b, a.Y - b, a.Z - b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(float a, Vector3 b) + { + return new Vector3(a - b.X, a - b.Y, a - b.Z); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator *(Vector3 a, Vector3 b) { @@ -175,6 +433,12 @@ public override string ToString() return new Vector3(a.X * b, a.Y * b, a.Z * b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(float a, Vector3 b) + { + return new Vector3(a * b.X, a * b.Y, a * b.Z); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator /(Vector3 a, Vector3 b) { @@ -193,6 +457,12 @@ public override string ToString() return new Vector3(a.X / b, a.Y / b, a.Z / b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(float a, Vector3 b) + { + return new Vector3(a / b.X, a / b.Y, a / b.Z); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector3 a, Vector3 b) { @@ -206,11 +476,143 @@ public override string ToString() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Cross(Vector3 left, Vector3 right) + public static float DistanceSquared(Vector2 valueA, Vector2 valueB) + { + return (valueA - valueB).LengthSquared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Distance(Vector2 valueA, Vector2 valueB) + { + return (valueA - valueB).Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Cross(Vector3 valueA, Vector3 valueB) + { + return new Vector3( + valueA.Y * valueB.Z - valueA.Z * valueB.Y, + valueA.Z * valueB.X - valueA.X * valueB.Z, + valueA.X * valueB.Y - valueA.Y * valueB.X); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector3 valueA, Vector3 valueB) + { + return valueA.X * valueB.X + valueA.Y * valueB.Y + valueA.Z * valueB.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Reflect(Vector3 value, Vector3 normal) + { + return value - 2.0f * (Dot(value, normal) * normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 MoveTowards(Vector3 current, Vector3 target, float distance) + { + return new Vector3( + HyperMathF.MoveTowards(current.X, target.X, distance), + HyperMathF.MoveTowards(current.Y, target.Y, distance), + HyperMathF.MoveTowards(current.Z, target.Z, distance)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Clamp(Vector3 value, Vector3 min, Vector3 max) + { + return new Vector3( + float.Clamp(value.X, min.X, max.X), + float.Clamp(value.Y, min.Y, max.Y), + float.Clamp(value.Z, min.Z, max.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Clamp(Vector3 value, float min, float max) + { + return new Vector3( + float.Clamp(value.X, min, max), + float.Clamp(value.Y, min, max), + float.Clamp(value.Z, min, max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Lerp(Vector3 value, Vector3 target, float amount) + { + return new Vector3( + float.Lerp(value.X, target.X, amount), + float.Lerp(value.Y, target.Y, amount), + float.Lerp(value.Z, target.Z, amount)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Max(Vector3 valueA, Vector3 valueB) + { + return new Vector3( + MathF.Max(valueA.X, valueB.X), + MathF.Max(valueA.Y, valueB.Y), + MathF.Max(valueA.Z, valueB.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Min(Vector3 valueA, Vector3 valueB) + { + return new Vector3( + MathF.Min(valueA.X, valueB.X), + MathF.Min(valueA.Y, valueB.Y), + MathF.Min(valueA.Z, valueB.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Abs(Vector3 value) + { + return new Vector3( + Math.Abs(value.X), + Math.Abs(value.Y), + Math.Abs(value.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Round(Vector3 value) + { + return new Vector3( + Math.Round(value.X), + Math.Round(value.Y), + Math.Round(value.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Round(Vector3 value, int digits) + { + return new Vector3( + Math.Round(value.X, digits), + Math.Round(value.Y, digits), + Math.Round(value.Z, digits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Ceiling(Vector3 value) + { + return new Vector3( + Math.Ceiling(value.X), + Math.Ceiling(value.Y), + Math.Ceiling(value.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Floor(Vector3 value) + { + return new Vector3( + Math.Floor(value.X), + Math.Floor(value.Y), + Math.Floor(value.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Sign(Vector3 value) { return new Vector3( - left.Y * right.Z - left.Z * right.Y, - left.Z * right.X - left.X * right.Z, - left.X * right.Y - left.Y * right.X); + Math.Sign(value.X), + Math.Sign(value.Y), + Math.Sign(value.Z)); } } \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector3i.Compability.cs b/Hypercube.Mathematics/Vectors/Vector3i.Compability.cs new file mode 100644 index 0000000..bafecab --- /dev/null +++ b/Hypercube.Mathematics/Vectors/Vector3i.Compability.cs @@ -0,0 +1,82 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Hypercube.Mathematics.Vectors; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public readonly partial struct Vector3i +{ + /* + * Self Compatibility + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector3(Vector3i value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /* + * Tuple Compatibility + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector3i((int x, int y, int z) value) + { + return new Vector3i(value.x, value.y, value.z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator (int x, int y, int z)(Vector3i value) + { + return (value.X, value.Y, value.Z); + } + + /* + * System.Numerics Compatibility + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector3i(System.Numerics.Vector3 value) + { + return new Vector3i((int) value.X, (int) value.Y, (int) value.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator System.Numerics.Vector3(Vector3i value) + { + return new System.Numerics.Vector3(value.X, value.Y, value.Z); + } + + /* + * OpenTK Compatibility + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector3i(OpenTK.Mathematics.Vector3i value) + { + return new Vector3i(value.X, value.Y, value.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator OpenTK.Mathematics.Vector3i(Vector3i value) + { + return new OpenTK.Mathematics.Vector3i(value.X, value.Y, value.Z); + } + + /* + * OpenToolkit Compatibility + */ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector3i(OpenToolkit.Mathematics.Vector3i vector) + { + return new Vector3i(vector.X, vector.Y, vector.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator OpenToolkit.Mathematics.Vector3i(Vector3i vector) + { + return new OpenToolkit.Mathematics.Vector3i(vector.X, vector.Y, vector.Z); + } +} \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector3i.cs b/Hypercube.Mathematics/Vectors/Vector3i.cs new file mode 100644 index 0000000..489db42 --- /dev/null +++ b/Hypercube.Mathematics/Vectors/Vector3i.cs @@ -0,0 +1,497 @@ +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using JetBrains.Annotations; + +namespace Hypercube.Mathematics.Vectors; + +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential), DebuggerDisplay("({X}, {Y}, {Z})")] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public readonly partial struct Vector3i : IEquatable, IComparable, IComparable, IEnumerable +{ + /// + /// Vector (0, 0, 0). + /// + public static readonly Vector3i Zero = new(0, 0, 0); + + /// + /// Vector (1, 1, 1). + /// + public static readonly Vector3i One = new(1, 1, 1); + + /// + /// Vector (1, 0, 0). + /// + public static readonly Vector3i UnitX = new(1, 0, 0); + + /// + /// Vector (0, 1, 0). + /// + public static readonly Vector3i UnitY = new(0, 1, 0); + + /// + /// Vector (0, 0, 1). + /// + public static readonly Vector3i UnitZ = new(0, 0, 1); + + /// + /// Vector X component. + /// + public readonly int X; + + /// + /// Vector Y component. + /// + public readonly int Y; + + /// + /// Vector Z component. + /// + public readonly int Z; + + /// + /// Gets the square of the vector length (magnitude). + /// + /// + /// Allows you to avoid using the rather expensive sqrt operation. + /// (On ARM64 hardware may use the FRSQRTE instruction, which would take away this advantage). + /// + /// + public float LengthSquared + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * X + Y * Y + Z * Z; + } + + /// + /// Gets the length (magnitude) of the vector. + /// + /// + /// On ARM64 hardware this may use the FRSQRTE instruction + /// which performs a single Newton-Raphson iteration. + /// On hardware without specialized support + /// sqrt is used, which makes the method less fast. + /// + /// + public float Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => 1f / MathF.ReciprocalSqrtEstimate(LengthSquared); + } + + /// + /// Summation of all vector components. + /// + public int Summation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X + Y + Z; + } + + /// + /// Production of all vector components. + /// + public int Production + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * Y * Z; + } + + public Vector2i XY + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(X, Y); + } + + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return index switch + { + 0 => X, + 1 => Y, + 2 => Z, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + public Vector3i(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + + public Vector3i(int value) + { + X = value; + Y = value; + Z = value; + } + + public Vector3i(Vector2i value) + { + X = value.X; + Y = value.Y; + Z = 0; + } + + public Vector3i(Vector2i value, int z) + { + X = value.X; + Y = value.Y; + Z = z; + } + + public Vector3i(Vector3i value) + { + X = value.X; + Y = value.Y; + Z = value.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i WithX(int value) + { + return new Vector3i(value, Y, Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i WithY(int value) + { + return new Vector3i(X, value, Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i WithZ(int value) + { + return new Vector3i(X, Y, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float DistanceSquared(Vector3i value) + { + return DistanceSquared(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Distance(Vector3i value) + { + return Distance(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Cross(Vector3i value) + { + return Cross(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Dot(Vector3i value) + { + return Dot(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Reflect(Vector3i normal) + { + return Reflect(this, normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i MoveTowards(Vector3i target, int distance) + { + return MoveTowards(this, target, distance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Clamp(Vector3i min, Vector3i max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Clamp(int min, int max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Max(Vector3i value) + { + return Max(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Min(Vector3i value) + { + return Min(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Abs() + { + return Abs(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3i Sign() + { + return Sign(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(int other) + { + return LengthSquared.CompareTo(other * other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Vector3i other) + { + return LengthSquared.CompareTo(other.LengthSquared); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerator GetEnumerator() + { + yield return X; + yield return Y; + yield return Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Vector3i other) + { + return X == other.X && + Y == other.Y && + Z == other.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) + { + return obj is Vector3i other && Equals(other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return HashCode.Combine(X, Y, Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() + { + return $"{X}, {Y}, {Z}"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator +(Vector3i a, Vector3i b) + { + return new Vector3i(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator +(Vector3i a, Vector2i b) + { + return new Vector3i(a.X + b.X, a.Y + b.Y, a.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator +(Vector3i a, int b) + { + return new Vector3i(a.X + b, a.Y + b, a.Z + b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator -(Vector3i a) + { + return new Vector3i(-a.X, -a.Y, -a.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator -(Vector3i a, Vector3i b) + { + return new Vector3i(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator -(Vector3i a, Vector2i b) + { + return new Vector3i(a.X - b.X, a.Y - b.Y, a.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator -(Vector3i a, int b) + { + return new Vector3i(a.X - b, a.Y - b, a.Z - b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator *(Vector3i a, Vector3i b) + { + return new Vector3i(a.X * b.X, a.Y * b.Y, a.Z * b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator *(Vector3i a, Vector2i b) + { + return new Vector3i(a.X * b.X, a.Y * b.Y, a.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator *(Vector3i a, int b) + { + return new Vector3i(a.X * b, a.Y * b, a.Z * b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator *(int a, Vector3i b) + { + return new Vector3i(a * b.X, a * b.Y, a * b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator /(Vector3i a, Vector3i b) + { + return new Vector3i(a.X / b.X, a.Y / b.Y, a.Z / b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator /(Vector3i a, Vector2i b) + { + return new Vector3i(a.X / b.X, a.Y / b.Y, a.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator /(Vector3i a, int b) + { + return new Vector3i(a.X / b, a.Y / b, a.Z / b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i operator /(int a, Vector3i b) + { + return new Vector3i(a / b.X, a / b.Y, a / b.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Vector3i a, Vector3i b) + { + return a.Equals(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Vector3i a, Vector3i b) + { + return !a.Equals(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistanceSquared(Vector3i valueA, Vector3i valueB) + { + return (valueA - valueB).LengthSquared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Distance(Vector3i valueA, Vector3i valueB) + { + return (valueA - valueB).Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Cross(Vector3i valueA, Vector3i valueB) + { + return new Vector3i( + valueA.Y * valueB.Z - valueA.Z * valueB.Y, + valueA.Z * valueB.X - valueA.X * valueB.Z, + valueA.X * valueB.Y - valueA.Y * valueB.X); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Dot(Vector3i valueA, Vector3i valueB) + { + return valueA.X * valueB.X + valueA.Y * valueB.Y + valueA.Z * valueB.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Reflect(Vector3i value, Vector3i normal) + { + return value - 2 * (Dot(value, normal) * normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i MoveTowards(Vector3i current, Vector3i target, int distance) + { + return new Vector3i( + HyperMath.MoveTowards(current.X, target.X, distance), + HyperMath.MoveTowards(current.Y, target.Y, distance), + HyperMath.MoveTowards(current.Z, target.Z, distance)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Clamp(Vector3i value, Vector3i min, Vector3i max) + { + return new Vector3i( + int.Clamp(value.X, min.X, max.X), + int.Clamp(value.Y, min.Y, max.Y), + int.Clamp(value.Z, min.Z, max.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Clamp(Vector3i value, int min, int max) + { + return new Vector3i( + int.Clamp(value.X, min, max), + int.Clamp(value.Y, min, max), + int.Clamp(value.Z, min, max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Max(Vector3i valueA, Vector3i valueB) + { + return new Vector3i( + Math.Max(valueA.X, valueB.X), + Math.Max(valueA.Y, valueB.Y), + Math.Max(valueA.Z, valueB.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Min(Vector3i valueA, Vector3i valueB) + { + return new Vector3i( + Math.Min(valueA.X, valueB.X), + Math.Min(valueA.Y, valueB.Y), + Math.Min(valueA.Z, valueB.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Abs(Vector3i value) + { + return new Vector3i( + Math.Abs(value.X), + Math.Abs(value.Y), + Math.Abs(value.Z)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3i Sign(Vector3i value) + { + return new Vector3i( + Math.Sign(value.X), + Math.Sign(value.Y), + Math.Sign(value.Z)); + } +} \ No newline at end of file diff --git a/Hypercube.Mathematics/Vectors/Vector4.cs b/Hypercube.Mathematics/Vectors/Vector4.cs index 4bf58cb..6f2e875 100644 --- a/Hypercube.Mathematics/Vectors/Vector4.cs +++ b/Hypercube.Mathematics/Vectors/Vector4.cs @@ -1,46 +1,165 @@ -using System.Runtime.CompilerServices; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Hypercube.Mathematics.Extensions; +using JetBrains.Annotations; namespace Hypercube.Mathematics.Vectors; -[StructLayout(LayoutKind.Sequential)] -public readonly partial struct Vector4 : IEquatable +[PublicAPI, Serializable, StructLayout(LayoutKind.Sequential), DebuggerDisplay("({X}, {Y}, {Z}, {W})")] +public readonly partial struct Vector4 : IEquatable, IComparable, IComparable, IEnumerable { - public static readonly Vector4 Zero = new(0, 0, 0, 0); - public static readonly Vector4 One = new(1, 1, 1, 1); - + /// + /// Vector (float.NaN, float.NaN, float.NaN, float.NaN). + /// + public static readonly Vector4 NaN = new(float.NaN); + + /// + /// Vector (float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity). + /// + public static readonly Vector4 PositiveInfinity = new(float.PositiveInfinity); + + /// + /// Vector (float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity). + /// + public static readonly Vector4 NegativeInfinity = new(float.NegativeInfinity); + + /// + /// Vector (0, 0, 0, 0). + /// + public static readonly Vector4 Zero = new(0); + + /// + /// Vector (1, 1, 1, 1). + /// + public static readonly Vector4 One = new(1); + + /// + /// Vector (1, 0, 0, 0). + /// public static readonly Vector4 UnitX = new(1, 0, 0, 0); + + /// + /// Vector (0, 1, 0, 0). + /// public static readonly Vector4 UnitY = new(0, 1, 0, 0); + + /// + /// Vector (0, 0, 1, 0). + /// public static readonly Vector4 UnitZ = new(0, 0, 1, 0); + + /// + /// Vector (0, 0, 0, 1). + /// public static readonly Vector4 UnitW = new(0, 0, 0, 1); + /// + /// Vector X component. + /// public readonly float X; + + /// + /// Vector Y component. + /// public readonly float Y; + + /// + /// Vector Z component. + /// public readonly float Z; + + /// + /// Vector W component. + /// public readonly float W; - public Vector2 XY => new(X, Y); - public Vector3 XYZ => new(X, Y, Z); - + /// + /// Gets the square of the vector length (magnitude). + /// + /// + /// Allows you to avoid using the rather expensive sqrt operation. + /// (On ARM64 hardware may use the FRSQRTE instruction, which would take away this advantage). + /// + /// public float LengthSquared { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => X * X + Y * Y + Z * Z + W * W; } + /// + /// Gets the length (magnitude) of the vector. + /// + /// + /// On ARM64 hardware this may use the FRSQRTE instruction + /// which performs a single Newton-Raphson iteration. + /// On hardware without specialized support + /// sqrt is used, which makes the method less fast. + /// + /// public float Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => MathF.Sqrt(LengthSquared); + get => 1f / MathF.ReciprocalSqrtEstimate(LengthSquared); } + /// + /// Copy of scaled to unit length. + /// public Vector4 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this / Length; } + /// + /// Summation of all vector components. + /// + public float Summation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X + Y + Z + W; + } + + /// + /// Production of all vector components. + /// + public float Production + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => X * Y * Z * W; + } + + public Vector2 XY + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(X, Y); + } + + public Vector3 XYZ + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(X, Y, Z); + } + + public float this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return index switch + { + 0 => X, + 1 => Y, + 2 => Z, + 3 => W, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + public Vector4(float x, float y, float z, float w) { X = x; @@ -49,34 +168,52 @@ public Vector4(float x, float y, float z, float w) W = w; } - public Vector4(float value) : this(value, value, value, value) + public Vector4(float value) { + X = value; + Y = value; + Z = value; + W = value; } - public Vector4(Vector2 vector2, float z, float w) : this(vector2.X, vector2.Y, z, w) + public Vector4(double x, double y, double z, double w) { + X = (float) x; + Y = (float) y; + Z = (float) z; + W = (float) w; } - public Vector4(Vector3 vector3, float w) : this(vector3.X, vector3.Y, vector3.Z, w) + public Vector4(double value) { + X = (float) value; + Y = (float) value; + Z = (float) value; + W = (float) value; } - - public Vector4(Vector4 vector4, float w) : this(vector4.X, vector4.Y, vector4.Z, w) + + public Vector4(Vector2 value, float z, float w) { + X = value.X; + Y = value.Y; + Z = z; + W = w; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Sum() + public Vector4(Vector3 value, float w) { - return X + Y + Z + W; + X = value.X; + Y = value.Y; + Z = value.Z; + W = w; } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Prod() + + public Vector4(Vector4 value) { - return X * Y * Z * W; + X = value.X; + Y = value.Y; + Z = value.Z; + W = value.W; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -103,6 +240,123 @@ public Vector4 WithW(float value) return new Vector4(X, Y, Z, value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float DistanceSquared(Vector4 value) + { + return DistanceSquared(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Distance(Vector4 value) + { + return Distance(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Dot(Vector4 value) + { + return Dot(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Reflect(Vector4 normal) + { + return Reflect(this, normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 MoveTowards(Vector4 target, float distance) + { + return MoveTowards(this, target, distance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Clamp(Vector4 min, Vector4 max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Clamp(float min, float max) + { + return Clamp(this, min, max); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Lerp(Vector4 value, float amount) + { + return Lerp(this, value, amount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Max(Vector4 value) + { + return Max(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Min(Vector4 value) + { + return Min(this, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Abs() + { + return Abs(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Round() + { + return Round(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Round(int digits) + { + return Round(this, digits); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Ceiling() + { + return Ceiling(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Floor() + { + return Floor(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Vector4 other) + { + return LengthSquared.CompareTo(other.LengthSquared); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(float other) + { + return LengthSquared.CompareTo(other * other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerator GetEnumerator() + { + yield return X; + yield return Y; + yield return Z; + yield return W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector4 other) { @@ -111,14 +365,13 @@ public bool Equals(Vector4 other) Z.AboutEquals(other.Z) && W.AboutEquals(other.W); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) { return obj is Vector4 other && Equals(other); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { @@ -155,6 +408,12 @@ public override int GetHashCode() return new Vector4(a.X + b, a.Y + b, a.Z + b, a.W + b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator +(float a, Vector4 b) + { + return new Vector4(a + b.X, a + b.Y, a + b.Z, a + b.W); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 operator -(Vector4 a) { @@ -185,6 +444,12 @@ public override int GetHashCode() return new Vector4(a.X - b, a.Y - b, a.Z - b, a.W - b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator -(float a, Vector4 b) + { + return new Vector4(a - b.X, a - b.Y, a - b.Z, a - b.W); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 operator *(Vector4 a, Vector4 b) { @@ -209,6 +474,12 @@ public override int GetHashCode() return new Vector4(a.X * b, a.Y * b, a.Z * b, a.W * b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator *(float a, Vector4 b) + { + return new Vector4(a * b.X, a * b.Y, a * b.Z, a * b.W); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 operator /(Vector4 a, Vector4 b) { @@ -233,6 +504,12 @@ public override int GetHashCode() return new Vector4(a.X / b, a.Y / b, a.Z / b, a.W / b); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator /(float a, Vector4 b) + { + return new Vector4(a / b.X, a / b.Y, a / b.Z, a / b.W); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector4 a, Vector4 b) { @@ -244,4 +521,151 @@ public override int GetHashCode() { return !a.Equals(b); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistanceSquared(Vector4 valueA, Vector4 valueB) + { + return (valueA - valueB).LengthSquared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Distance(Vector4 valueA, Vector4 valueB) + { + return (valueA - valueB).Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector4 valueA, Vector4 valueB) + { + return valueA.X * valueB.X + + valueA.Y * valueB.Y + + valueA.Z * valueB.Z + + valueA.W * valueB.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Reflect(Vector4 value, Vector4 normal) + { + return value - 2.0f * (Dot(value, normal) * normal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MoveTowards(Vector4 current, Vector4 target, float distance) + { + return new Vector4( + HyperMathF.MoveTowards(current.X, target.X, distance), + HyperMathF.MoveTowards(current.Y, target.Y, distance), + HyperMathF.MoveTowards(current.Z, target.Z, distance), + HyperMathF.MoveTowards(current.W, target.W, distance)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) + { + return new Vector4( + float.Clamp(value.X, min.X, max.X), + float.Clamp(value.Y, min.Y, max.Y), + float.Clamp(value.Z, min.Z, max.Z), + float.Clamp(value.W, min.W, max.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Clamp(Vector4 value, float min, float max) + { + return new Vector4( + float.Clamp(value.X, min, max), + float.Clamp(value.Y, min, max), + float.Clamp(value.Z, min, max), + float.Clamp(value.W, min, max)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Lerp(Vector4 value, Vector4 target, float amount) + { + return new Vector4( + float.Lerp(value.X, target.X, amount), + float.Lerp(value.Y, target.Y, amount), + float.Lerp(value.Z, target.Z, amount), + float.Lerp(value.W, target.W, amount)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Max(Vector4 valueA, Vector4 valueB) + { + return new Vector4( + MathF.Max(valueA.X, valueB.X), + MathF.Max(valueA.Y, valueB.Y), + MathF.Max(valueA.Z, valueB.Z), + MathF.Max(valueA.W, valueB.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Min(Vector4 valueA, Vector4 valueB) + { + return new Vector4( + MathF.Min(valueA.X, valueB.X), + MathF.Min(valueA.Y, valueB.Y), + MathF.Min(valueA.Z, valueB.Z), + MathF.Min(valueA.W, valueB.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Abs(Vector4 value) + { + return new Vector4( + Math.Abs(value.X), + Math.Abs(value.Y), + Math.Abs(value.Z), + Math.Abs(value.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Round(Vector4 value) + { + return new Vector4( + Math.Round(value.X), + Math.Round(value.Y), + Math.Round(value.Z), + Math.Round(value.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Round(Vector4 value, int digits) + { + return new Vector4( + Math.Round(value.X, digits), + Math.Round(value.Y, digits), + Math.Round(value.Z, digits), + Math.Round(value.W, digits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Ceiling(Vector4 value) + { + return new Vector4( + Math.Ceiling(value.X), + Math.Ceiling(value.Y), + Math.Ceiling(value.Z), + Math.Ceiling(value.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Floor(Vector4 value) + { + return new Vector4( + Math.Floor(value.X), + Math.Floor(value.Y), + Math.Floor(value.Z), + Math.Floor(value.W)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Sign(Vector4 value) + { + return new Vector4( + Math.Sign(value.X), + Math.Sign(value.Y), + Math.Sign(value.Z), + Math.Sign(value.W)); + } } \ No newline at end of file diff --git a/Hypercube.OpenGL/Shaders/ShaderProgram.cs b/Hypercube.OpenGL/Shaders/ShaderProgram.cs index 298db1b..e6b3c1a 100644 --- a/Hypercube.OpenGL/Shaders/ShaderProgram.cs +++ b/Hypercube.OpenGL/Shaders/ShaderProgram.cs @@ -97,7 +97,7 @@ public void SetUniform(string name, Vector2 value) GL.Uniform2(_uniformLocations[name], value.X, value.Y); } - public void SetUniform(string name, Vector2Int value) + public void SetUniform(string name, Vector2i value) { GL.Uniform2(_uniformLocations[name], value.X, value.Y); } diff --git a/Hypercube.OpenGL/Utilities/Helpers/GLFWHelper.cs b/Hypercube.OpenGL/Utilities/Helpers/GLFWHelper.cs index 90e0caa..e065038 100644 --- a/Hypercube.OpenGL/Utilities/Helpers/GLFWHelper.cs +++ b/Hypercube.OpenGL/Utilities/Helpers/GLFWHelper.cs @@ -6,28 +6,28 @@ namespace Hypercube.OpenGL.Utilities.Helpers; public static unsafe class GLFWHelper { - public static void GetFramebufferSize(Window* window, out Vector2Int framebufferSize) + public static void GetFramebufferSize(Window* window, out Vector2i framebufferSize) { GLFW.GetFramebufferSize(window, out var x, out var y); - framebufferSize = new Vector2Int(x, y); + framebufferSize = new Vector2i(x, y); } - public static void GetFramebufferSize(nint window, out Vector2Int framebufferSize) + public static void GetFramebufferSize(nint window, out Vector2i framebufferSize) { GLFW.GetFramebufferSize((Window*)window, out var x, out var y); - framebufferSize = new Vector2Int(x, y); + framebufferSize = new Vector2i(x, y); } - public static void GetWindowSize(Window* window, out Vector2Int size) + public static void GetWindowSize(Window* window, out Vector2i size) { GLFW.GetWindowSize(window, out var x, out var y); - size = new Vector2Int(x, y); + size = new Vector2i(x, y); } - public static void GetWindowSize(nint window, out Vector2Int size) + public static void GetWindowSize(nint window, out Vector2i size) { GLFW.GetWindowSize((Window*)window, out var x, out var y); - size = new Vector2Int(x, y); + size = new Vector2i(x, y); } public static string FormatError(ErrorCode error, string description) diff --git a/Hypercube.Shared/Physics/Chunk.cs b/Hypercube.Shared/Physics/Chunk.cs index 8475262..9e35b24 100644 --- a/Hypercube.Shared/Physics/Chunk.cs +++ b/Hypercube.Shared/Physics/Chunk.cs @@ -10,14 +10,14 @@ namespace Hypercube.Shared.Physics; /// public sealed class Chunk { - public readonly Vector2Int Position; + public readonly Vector2i Position; private HashSet _bodies = new(); public IReadOnlySet Bodies => _bodies; public bool ContainBodes => _bodies.Count != 0; - public Chunk(Vector2Int position) + public Chunk(Vector2i position) { Position = position; } diff --git a/Hypercube.UnitTests/Math/AngleTest.cs b/Hypercube.UnitTests/Math/AngleTest.cs index ba4461e..8a28895 100644 --- a/Hypercube.UnitTests/Math/AngleTest.cs +++ b/Hypercube.UnitTests/Math/AngleTest.cs @@ -3,10 +3,20 @@ namespace Hypercube.UnitTests.Math; +[TestFixture] public static class AngleTest { + private static readonly Dictionary ConvertToVectorPairs = new() + { + { 0d, Vector2.UnitX }, + { HyperMath.PIOver2, Vector2.UnitY }, + { HyperMath.PIOver4, Vector2.One.Normalized }, + { HyperMath.PI, -Vector2.UnitX }, + { HyperMath.ThreePiOver2, -Vector2.UnitY }, + }; + [Test] - public static void Degrees() + public static void ConvertToDegrees() { Assert.Multiple(() => { @@ -14,41 +24,33 @@ public static void Degrees() Assert.That(new Angle(HyperMath.PIOver2).Degrees, Is.EqualTo(90d).Within(0.01d)); Assert.That(new Angle(HyperMath.PIOver4).Degrees, Is.EqualTo(45d).Within(0.01d)); Assert.That(new Angle(HyperMath.PIOver6).Degrees, Is.EqualTo(30d).Within(0.01d)); - + }); + } + + [Test] + public static void ConvertFromDegrees() + { + Assert.Multiple(() => + { Assert.That(Angle.FromDegrees(180d), Is.EqualTo(new Angle(HyperMath.PI))); Assert.That(Angle.FromDegrees(90d), Is.EqualTo(new Angle(HyperMath.PIOver2))); Assert.That(Angle.FromDegrees(45d), Is.EqualTo(new Angle(HyperMath.PIOver4))); Assert.That(Angle.FromDegrees(30d), Is.EqualTo(new Angle(HyperMath.PIOver6))); }); - - Assert.Pass($"{nameof(Angle)} degrees passed"); } [Test] - public static void Vector() + public static void ConvertToVector() { - Assert.Multiple(() => + foreach (var (angle, vector) in ConvertToVectorPairs) { - Assert.That(Angle.Zero.GetRoundVector(), Is.EqualTo(Vector2.UnitX)); - Assert.That(new Angle(HyperMath.PI).GetRoundVector(), Is.EqualTo(-Vector2.UnitX)); - Assert.That(new Angle(-HyperMath.PI).GetRoundVector(), Is.EqualTo(-Vector2.UnitX)); + var vectorA = new Angle(angle).Vector.Round(3); + var vectorB = vector.Round(3); + + if (vectorA.Equals(vectorB)) + continue; - Assert.That(new Angle(HyperMath.PIOver2).GetRoundVector(), Is.EqualTo(Vector2.UnitY)); - Assert.That(new Angle(HyperMath.ThreePiOver2).GetRoundVector(), Is.EqualTo(-Vector2.UnitY)); - Assert.That(new Angle(-HyperMath.PIOver2).GetRoundVector(), Is.EqualTo(-Vector2.UnitY)); - - Assert.That(new Angle(HyperMath.PIOver4).GetRoundVector(), Is.EqualTo(Vector2.One.Normalized)); - Assert.That(new Angle(HyperMath.ThreePiOver2 - HyperMath.PIOver4).GetRoundVector(), Is.EqualTo(-Vector2.One.Normalized)); - }); - - Assert.Pass($"{nameof(Angle)} vector passed"); - } - - private static Vector2 GetRoundVector(this Angle angle) - { - var vector = angle.Vector; - var x = MathF.Abs(vector.X) < 1e-15f ? 0 : vector.X; - var y = MathF.Abs(vector.Y) < 1e-15f ? 0 : vector.Y; - return new Vector2(x, y); + Assert.Fail($"Expected {vectorB}, but was {vectorA}"); + } } } \ No newline at end of file diff --git a/Hypercube.UnitTests/Math/CompareToTest.cs b/Hypercube.UnitTests/Math/CompareToTest.cs index 6f716ee..9db32c0 100644 --- a/Hypercube.UnitTests/Math/CompareToTest.cs +++ b/Hypercube.UnitTests/Math/CompareToTest.cs @@ -1,6 +1,4 @@ -using System; -using Hypercube.Math.Vectors; -using NUnit.Framework; +using Hypercube.Mathematics.Vectors; namespace Hypercube.UnitTests.Math; @@ -18,79 +16,6 @@ public void CompareTo_LengthComparison_ReturnsExpectedResult() var result = vector1.CompareTo(vector2); // Assert - Assert.Less(result, 0); - } - - [Test] - public void CompareTo_XComponentComparison_ReturnsExpectedResult() - { - // Arrange - var vector1 = new Vector2(3, 4); - var vector2 = new Vector2(6, 4); - - // Act - var result = vector1.CompareTo(vector2, v => v.X); - - // Assert - Assert.Less(result, 0); // Expecting vector1 to be "less than" vector2 based on X component - } - - [Test] - public void CompareTo_YComponentComparison_ReturnsExpectedResult() - { - // Arrange - var vector1 = new Vector2(3, 4); - var vector2 = new Vector2(3, 2); - - // Act - var result = vector1.CompareTo(vector2, v => v.Y); - - // Assert - Assert.Greater(result, 0); // Expecting vector1 to be "greater than" vector2 based on Y component - } - - [Test] - public void CompareTo_AngleComparison_ReturnsExpectedResult() - { - // Arrange - var vector1 = new Vector2(0, 1); // Angle = π/2 (90 degrees) - var vector2 = new Vector2(1, 0); // Angle = 0 degrees - - // Act - var result = vector1.CompareTo(vector2, v => v.Angle); - - // Assert - Assert.Greater(result, 0); // Expecting vector1 to be "greater than" vector2 based on Angle - } - - [Test] - public void CompareTo_SameVectors_ReturnsZero() - { - // Arrange - var vector1 = new Vector2(3, 4); - var vector2 = new Vector2(3, 4); - - // Act & Assert - Assert.AreEqual(0, vector1.CompareTo(vector2)); - Assert.AreEqual(0, vector1.CompareTo(vector2, v => v.X)); - Assert.AreEqual(0, vector1.CompareTo(vector2, v => v.Y)); - Assert.AreEqual(0, vector1.CompareTo(vector2, v => v.Angle)); - } - - [Test] - public void CompareTo_DifferentComparisons_ReturnDifferentResults() - { - // Arrange - var vector1 = new Vector2(1, 2); - var vector2 = new Vector2(2, 1); - - // Act - var lengthComparison = vector1.CompareTo(vector2); - var xComponentComparison = vector1.CompareTo(vector2, v => v.X); - var yComponentComparison = vector1.CompareTo(vector2, v => v.Y); - - // Assert - Assert.AreNotEqual(lengthComparison, xComponentComparison); - Assert.AreNotEqual(xComponentComparison, yComponentComparison); + Assert.That(result, Is.LessThan(0)); } } \ No newline at end of file diff --git a/Hypercube.sln.DotSettings b/Hypercube.sln.DotSettings index dd580fd..316113c 100644 --- a/Hypercube.sln.DotSettings +++ b/Hypercube.sln.DotSettings @@ -1,2 +1,5 @@  + True + True + True True \ No newline at end of file