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);
+}