From d42db4c4766d75be18720f5512a17ef1d2b48411 Mon Sep 17 00:00:00 2001 From: Hamlet Tanyavong <34531738+HamletTanyavong@users.noreply.github.com> Date: Sun, 26 Nov 2023 18:08:25 -0600 Subject: [PATCH] Create CylindricalVector.cs --- .../LinearAlgebra/CylindricalVector.cs | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/Mathematics.NET/LinearAlgebra/CylindricalVector.cs diff --git a/src/Mathematics.NET/LinearAlgebra/CylindricalVector.cs b/src/Mathematics.NET/LinearAlgebra/CylindricalVector.cs new file mode 100644 index 00000000..993563d9 --- /dev/null +++ b/src/Mathematics.NET/LinearAlgebra/CylindricalVector.cs @@ -0,0 +1,208 @@ +// +// Mathematics.NET +// https://github.com/HamletTanyavong/Mathematics.NET +// +// MIT License +// +// Copyright (c) 2023 Hamlet Tanyavong +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mathematics.NET.LinearAlgebra.Abstractions; + +namespace Mathematics.NET.LinearAlgebra; + +/// Represents a 3D vector in cylindrical coordinates +[StructLayout(LayoutKind.Sequential)] +public struct CylindricalVector : IVector +{ + /// The radial distance + public Real Rho; + + /// The azimuthal angle + public Real Phi; + + /// The axial coordinate + public Real Z; + + /// Create a 3D vector in cylindrical coordinates + /// The radial distance + /// The azimuthal angle + /// The axial coordinate + /// Thrown when a negative value is supplied for the radial distance + public CylindricalVector(Real rho, Real phi, Real z) + { + if (rho < 0) + { + throw new ArgumentException("Rho must not be negative"); + } + + Rho = rho; + Phi = phi; + Z = z; + } + + // + // IArrayRepresentable & relevant interfaces + // + + public static int Components => 3; + + public static int E1Components => 3; + + // + // Indexer + // + + public Real this[int index] + { + get => GetElement(this, index); + set => this = WithElement(this, index, value); + } + + // Get + + internal static Real GetElement(CylindricalVector vector, int index) + { + if ((uint)index >= 3) + { + throw new IndexOutOfRangeException(); + } + + return GetElementUnsafe(ref vector, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Real GetElementUnsafe(ref CylindricalVector vector, int index) + { + Debug.Assert(index is >= 0 and < 3); + return Unsafe.Add(ref Unsafe.As(ref vector), index); + } + + // Set + + internal static CylindricalVector WithElement(CylindricalVector vector, int index, Real value) + { + if ((uint)index >= 3) + { + throw new IndexOutOfRangeException(); + } + + CylindricalVector result = vector; + SetElementUnsafe(ref result, index, value); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetElementUnsafe(ref CylindricalVector vector, int index, Real value) + { + Debug.Assert(index is >= 0 and < 3); + Unsafe.Add(ref Unsafe.As(ref vector), index) = value; + } + + // + // Operators + // + + public static CylindricalVector operator +(CylindricalVector left, CylindricalVector right) + { + var x = left.Rho * Real.Cos(left.Phi) + right.Rho * Real.Cos(right.Phi); + var y = left.Rho * Real.Sin(left.Phi) + right.Rho * Real.Sin(right.Phi); + var z = left.Z + right.Z; + + return new(Real.Hypot(x, y), Real.Atan2(y, x), z); + } + + public static CylindricalVector operator -(CylindricalVector left, CylindricalVector right) + { + var x = left.Rho * Real.Cos(left.Phi) - right.Rho * Real.Cos(right.Phi); + var y = left.Rho * Real.Sin(left.Phi) - right.Rho * Real.Sin(right.Phi); + var z = left.Z - right.Z; + + return new(Real.Hypot(x, y), Real.Atan2(y, x), z); + } + + // + // Equality + // + + public static bool operator ==(CylindricalVector left, CylindricalVector right) + => left.Rho == right.Rho && left.Phi == right.Phi && left.Z == right.Z; + + public static bool operator !=(CylindricalVector left, CylindricalVector right) + => left.Rho != right.Rho || left.Phi != right.Phi || left.Z != right.Z; + + public override bool Equals([NotNullWhen(true)] object? obj) => obj is CylindricalVector other && Equals(other); + + public bool Equals(CylindricalVector other) + => Rho.Equals(other.Rho) && Phi.Equals(other.Phi) && Z.Equals(other.Z); + + public override readonly int GetHashCode() => HashCode.Combine(Rho, Phi, Z); + + // + // Formatting + // + + public string ToString(string? format, IFormatProvider? provider) => string.Format(provider, "({0}, {1}, {2})", + Rho.ToString(format, provider), + Phi.ToString(format, provider), + Z.ToString(format, provider)); + + // + // Methods + // + + /// + public static CylindricalVector Cross(CylindricalVector left, CylindricalVector right) + { + var x = left.Rho * Real.Sin(left.Phi) * right.Z - right.Rho * Real.Sin(right.Phi) * left.Z; + var y = right.Rho * Real.Cos(right.Phi) * left.Z - left.Rho * Real.Cos(left.Phi) * right.Z; + var z = -left.Rho * right.Rho * Real.Sin(left.Phi - right.Phi); + + return new(Real.Hypot(x, y), Real.Atan2(y, x), z); + } + + /// Convert a vector in cartesian cordinates to one in cylindrical coordinates. + /// The vector must have real components. + /// The vector to convert + /// A cylindrical vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CylindricalVector FromCartesian(Vector3 x) => new(Real.Hypot(x.X1, x.X2), Real.Atan2(x.X2, x.X1), x.X3); + + public static Real InnerProduct(CylindricalVector left, CylindricalVector right) + => left.Rho * right.Rho * Real.Cos(left.Phi - right.Phi) + left.Z * right.Z; + + public Real Norm() => Real.Hypot(Rho, Z); + + public CylindricalVector Normalize() + { + var norm = Real.Hypot(Rho, Z); + return new(Rho / norm, Phi, Z / norm); + } + + /// Convert a vector in cylindrical coordinates to one in Cartesian coordinates. + /// A vector in Cylindrical coordinates + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToCartesian() => new(Rho * Real.Cos(Phi), Rho * Real.Sin(Phi), Z); +}