Partial work on quaternions and some other tweaks.
This commit is contained in:
parent
664ba0fab7
commit
3a7a3c320e
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Nerd_STF.Helpers
|
||||
{
|
||||
public static class CordicHelper
|
||||
internal static class CordicHelper
|
||||
{
|
||||
// Starts at 4 radians. Each index downwards is half that.
|
||||
// Goes from 2^2 to 2^-19.
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Nerd_STF.Mathematics;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
@ -110,5 +111,44 @@ namespace Nerd_STF.Helpers
|
||||
}
|
||||
return total.ToString();
|
||||
}
|
||||
|
||||
private static readonly string dimNumSymbols = " ijk?";
|
||||
#if CS8_OR_GREATER
|
||||
public static string HighDimNumberToString(IEnumerable<double> terms, string? format, IFormatProvider? provider)
|
||||
#else
|
||||
public static string HighDimNumberToString(IEnumerable<double> terms, string format, IFormatProvider provider)
|
||||
#endif
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int index = 0;
|
||||
bool first = true;
|
||||
foreach (double term in terms)
|
||||
{
|
||||
if (term == 0)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (first) builder.Append(term.ToString(format, provider));
|
||||
else
|
||||
{
|
||||
if (term > 0)
|
||||
{
|
||||
builder.Append(" + ");
|
||||
builder.Append(term.ToString(format, provider));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(" - ");
|
||||
builder.Append((-term).ToString(format, provider));
|
||||
}
|
||||
}
|
||||
if (index > 0) builder.Append(dimNumSymbols[MathE.Min(index, dimNumSymbols.Length)]);
|
||||
first = false;
|
||||
index++;
|
||||
}
|
||||
if (first) builder.Append(0.0.ToString(format, provider));
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,9 @@ namespace Nerd_STF.Mathematics
|
||||
public interface IPresets4d<TSelf> : IPresets3d<TSelf>
|
||||
where TSelf : IPresets4d<TSelf>
|
||||
{
|
||||
static abstract TSelf LowW { get; }
|
||||
static abstract TSelf HighW { get; }
|
||||
// TODO: The HighW and LowW vectors could also be called "ana" and "kata."
|
||||
static abstract TSelf LowW { get; } // Kata
|
||||
static abstract TSelf HighW { get; } // Ana
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -260,7 +260,7 @@ namespace Nerd_STF.Mathematics
|
||||
public static bool operator ==(Int2 a, Int2 b) => a.Equals(b);
|
||||
public static bool operator !=(Int2 a, Int2 b) => !a.Equals(b);
|
||||
|
||||
public static explicit operator Int2(Complex complex) => new Int2((int)complex.Real, (int)complex.Imaginary);
|
||||
public static explicit operator Int2(Complex complex) => new Int2((int)complex.r, (int)complex.i);
|
||||
public static explicit operator Int2(Float2 floats) => new Int2((int)floats.x, (int)floats.y);
|
||||
public static explicit operator Int2(Float3 floats) => new Int2((int)floats.x, (int)floats.y);
|
||||
public static explicit operator Int2(Float4 floats) => new Int2((int)floats.x, (int)floats.y);
|
||||
|
||||
@ -2,18 +2,21 @@
|
||||
using Nerd_STF.Helpers;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Numbers
|
||||
{
|
||||
public struct Complex : IEquatable<Complex>,
|
||||
IFormattable
|
||||
public struct Complex : IComparable<Complex>,
|
||||
IEquatable<Complex>,
|
||||
IFormattable,
|
||||
INumberGroup<Complex, double>
|
||||
#if CS11_OR_GREATER
|
||||
,INumber<Complex>,
|
||||
IFromTuple<Complex, (double, double)>,
|
||||
IInterpolable<Complex>,
|
||||
IPresets2d<Complex>,
|
||||
IRoundable<Complex>,
|
||||
@ -55,6 +58,61 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
i = imaginary;
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return r;
|
||||
case 1: return i;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: r = value; break;
|
||||
case 1: i = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
double[] items = new double[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'r': items[i] = r; break;
|
||||
case 'i': items[i] = this.i; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
IEnumerator<double> stepper = value.GetEnumerator();
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
stepper.MoveNext();
|
||||
switch (c)
|
||||
{
|
||||
case 'r': r = stepper.Current; break;
|
||||
case 'i': this.i = stepper.Current; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CS8_OR_GREATER
|
||||
public static Complex Parse(string? str) =>
|
||||
#else
|
||||
@ -195,20 +253,19 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
public static Complex Floor(Complex num) =>
|
||||
new Complex(MathE.Floor(num.r),
|
||||
MathE.Floor(num.i));
|
||||
public static Complex Lerp(Complex a, Complex b, double t, bool clamp = false) =>
|
||||
public static Complex Lerp(Complex a, Complex b, double t, bool clamp = true) =>
|
||||
new Complex(MathE.Lerp(a.r, b.r, t, clamp),
|
||||
MathE.Lerp(a.i, b.i, t, clamp));
|
||||
public static Complex Product(IEnumerable<Complex> vals)
|
||||
{
|
||||
bool any = false;
|
||||
double resultR = 1, resultI = 1;
|
||||
Complex result = One;
|
||||
foreach (Complex val in vals)
|
||||
{
|
||||
any = true;
|
||||
resultR *= val.r;
|
||||
resultI *= val.i;
|
||||
result *= val;
|
||||
}
|
||||
return any ? new Complex(resultR, resultI) : Zero;
|
||||
return any ? result : Zero;
|
||||
}
|
||||
public static Complex Round(Complex val) =>
|
||||
new Complex(MathE.Round(val.r),
|
||||
@ -338,12 +395,20 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
{
|
||||
reals[index] = val.r;
|
||||
imaginaries[index] = val.i;
|
||||
index++;
|
||||
}
|
||||
return (reals, imaginaries);
|
||||
}
|
||||
|
||||
public int CompareTo(double other) => Magnitude.CompareTo(MathE.Abs(other));
|
||||
public int CompareTo(Complex other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return r;
|
||||
yield return i;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public int CompareTo(double other) => MagnitudeSqr.CompareTo(MathE.Abs(other * other));
|
||||
public int CompareTo(Complex other) => MagnitudeSqr.CompareTo(other.MagnitudeSqr);
|
||||
#if CS8_OR_GREATER
|
||||
public int CompareTo(object? other)
|
||||
#else
|
||||
@ -352,8 +417,9 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
{
|
||||
if (other is null) return 1;
|
||||
else if (other is Complex otherComplex) return CompareTo(otherComplex);
|
||||
else if (other is double otherDouble) return CompareTo(otherDouble);
|
||||
else if (TryConvertFrom(other, out Complex otherConvert)) return CompareTo(otherConvert);
|
||||
else return 0;
|
||||
else return 1;
|
||||
}
|
||||
public bool Equals(double other) => r == other && i == 0;
|
||||
public bool Equals(Complex other) => r == other.r && i == other.i;
|
||||
@ -369,41 +435,24 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
public override string ToString() => ToString(null, null);
|
||||
public override string ToString() => ToStringHelper.HighDimNumberToString(this, null, null);
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format) => ToString(format, null);
|
||||
public string ToString(IFormatProvider? provider) => ToString(null, provider);
|
||||
public string ToString(string? format) => ToStringHelper.HighDimNumberToString(this, format, null);
|
||||
public string ToString(IFormatProvider? provider) => ToStringHelper.HighDimNumberToString(this, null, provider);
|
||||
public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.HighDimNumberToString(this, format, provider);
|
||||
#else
|
||||
public string ToString(string format) => ToString(format, null);
|
||||
public string ToString(IFormatProvider provider) => ToString(null, provider);
|
||||
#endif
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format, IFormatProvider? provider)
|
||||
#else
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
public string ToString(string format) => ToStringHelper.HighDimNumberToString(this, format, null);
|
||||
public string ToString(IFormatProvider provider) => ToStringHelper.HighDimNumberToString(this, null, provider);
|
||||
public string ToString(string format, IFormatProvider provider) => ToStringHelper.HighDimNumberToString(this, format, provider);
|
||||
#endif
|
||||
|
||||
public double[] ToArray() => new double[] { r, i };
|
||||
public Fill<double> ToFill()
|
||||
{
|
||||
if (r == 0 && i == 0) return 0.0.ToString(format, provider);
|
||||
else if (r == 0) return $"{i.ToString(format, provider)}i";
|
||||
else
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append(r.ToString(format, provider));
|
||||
if (i > 0)
|
||||
{
|
||||
builder.Append(" + ");
|
||||
builder.Append(i.ToString(format, provider));
|
||||
builder.Append('i');
|
||||
}
|
||||
else if (i < 0)
|
||||
{
|
||||
builder.Append(" - ");
|
||||
builder.Append((-i).ToString(format, provider));
|
||||
builder.Append('i');
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
Complex @this = this;
|
||||
return i => @this[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double>() { r, i };
|
||||
|
||||
#if CS11_OR_GREATER
|
||||
public bool TryFormat(Span<char> dest, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
|
||||
@ -414,16 +463,17 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
charsWritten = result.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Complex IIncrementOperators<Complex>.operator ++(Complex a) => new Complex(a.r + 1, a.i);
|
||||
static Complex IDecrementOperators<Complex>.operator --(Complex a) => new Complex(a.r - 1, a.i);
|
||||
#endif
|
||||
|
||||
public static Complex operator +(Complex a) => a;
|
||||
public static Complex operator +(Complex a, Complex b) => new Complex(a.r + b.r, a.i + b.i);
|
||||
public static Complex operator +(Complex a, double b) => new Complex(a.r + b, a.i);
|
||||
public static Complex operator ++(Complex a) => new Complex(a.r + 1, a.i);
|
||||
public static Complex operator -(Complex a) => new Complex(-a.r, -a.i);
|
||||
public static Complex operator -(Complex a, Complex b) => new Complex(a.r - b.r, a.i - b.i);
|
||||
public static Complex operator -(Complex a, double b) => new Complex(a.r - b, a.i);
|
||||
public static Complex operator --(Complex a) => new Complex(a.r - 1, a.i);
|
||||
public static Complex operator *(Complex a, Complex b) => new Complex(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r);
|
||||
public static Complex operator *(Complex a, double b) => new Complex(a.r * b, a.i * b);
|
||||
public static Complex operator /(Complex a, Complex b)
|
||||
@ -447,9 +497,14 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
public static bool operator <=(Complex a, Complex b) => a.CompareTo(b) <= 0;
|
||||
|
||||
public static implicit operator Complex(System.Numerics.Complex num) => new Complex(num.Real, num.Imaginary);
|
||||
public static implicit operator System.Numerics.Complex(Complex num) => new Complex(num.r, num.i);
|
||||
public static implicit operator Complex(Float2 group) => new Complex(group.x, group.y);
|
||||
public static explicit operator Complex(Int2 group) => new Complex(group.x, group.y);
|
||||
public static implicit operator Complex(ListTuple<double> tuple) => new Complex(tuple[0], tuple[1]);
|
||||
public static implicit operator Complex(ValueTuple<double, double> tuple) => new Complex(tuple.Item1, tuple.Item2);
|
||||
public static explicit operator Complex(Vector2 group) => new Complex(group.X, group.Y);
|
||||
|
||||
public static implicit operator System.Numerics.Complex(Complex num) => new Complex(num.r, num.i);
|
||||
public static implicit operator ListTuple<double>(Complex num) => new ListTuple<double>(num.r, num.i);
|
||||
public static implicit operator ValueTuple<double, double>(Complex num) => (num.r, num.i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,7 +325,7 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
return false;
|
||||
}
|
||||
else if (value is Fraction valueFrac) result = valueFrac;
|
||||
else if (value is Complex valueComp) result = Approximate(valueComp.Real);
|
||||
else if (value is Complex valueComp) result = Approximate(valueComp.r);
|
||||
else if (value is double valueDouble) result = Approximate(valueDouble);
|
||||
else if (value is float valueSingle) result = Approximate(valueSingle);
|
||||
#if NET5_0_OR_GREATER
|
||||
|
||||
327
Nerd_STF/Mathematics/Numbers/Quaternion.cs
Normal file
327
Nerd_STF/Mathematics/Numbers/Quaternion.cs
Normal file
@ -0,0 +1,327 @@
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Helpers;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Numbers
|
||||
{
|
||||
public struct Quaternion : IComparable<double>,
|
||||
IEnumerable<double>,
|
||||
IFormattable,
|
||||
INumberGroup<Quaternion, double>
|
||||
#if CS11_OR_GREATER
|
||||
,//INumber<Quaternion>,
|
||||
//IFromTuple<Quaternion, (double, double, double, double)>,
|
||||
IInterpolable<Quaternion>,
|
||||
IPresets4d<Quaternion>,
|
||||
IRoundable<Quaternion>,
|
||||
ISimpleMathOperations<Quaternion>,
|
||||
ISplittable<Quaternion, (double[] Ws, double[] Xs, double[] Ys, double[] Zs)>,
|
||||
IVectorOperations<Quaternion>
|
||||
#endif
|
||||
{
|
||||
public static Quaternion Backward => new Quaternion(0, 0, 0, -1);
|
||||
public static Quaternion Down => new Quaternion(0, 0, -1, 0);
|
||||
public static Quaternion Forward => new Quaternion(0, 0, 0, 1);
|
||||
public static Quaternion HighW => new Quaternion(1, 0, 0, 0);
|
||||
public static Quaternion Left => new Quaternion(0, -1, 0, 0);
|
||||
public static Quaternion LowW => new Quaternion(-1, 0, 0, 0);
|
||||
public static Quaternion Right => new Quaternion(0, 1, 0, 0);
|
||||
public static Quaternion Up => new Quaternion(0, 0, 1, 0);
|
||||
|
||||
public static Quaternion One => new Quaternion(1, 1, 1, 1);
|
||||
public static Quaternion Zero => new Quaternion(0, 0, 0, 0);
|
||||
|
||||
public Quaternion Conjugate => new Quaternion(w, -x, -y, -z);
|
||||
public double Magnitude => MathE.Sqrt(w * w + x * x + y * y + z * z);
|
||||
public double MagnitudeSqr => w * w + x * x + y * y + z * z;
|
||||
|
||||
public double w, x, y, z;
|
||||
|
||||
public Quaternion(double w, double x, double y, double z)
|
||||
{
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
public Quaternion(IEnumerable<double> nums)
|
||||
{
|
||||
w = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
|
||||
int index = 0;
|
||||
foreach (double item in nums)
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index == 4) break;
|
||||
}
|
||||
}
|
||||
public Quaternion(Fill<double> fill)
|
||||
{
|
||||
w = fill(0);
|
||||
x = fill(1);
|
||||
y = fill(2);
|
||||
z = fill(3);
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return w;
|
||||
case 1: return x;
|
||||
case 2: return y;
|
||||
case 3: return z;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: w = value; break;
|
||||
case 1: x = value; break;
|
||||
case 2: y = value; break;
|
||||
case 3: z = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
double[] items = new double[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'w': items[i] = w; break;
|
||||
case 'x': items[i] = x; break;
|
||||
case 'y': items[i] = y; break;
|
||||
case 'z': items[i] = z; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
IEnumerator<double> stepper = value.GetEnumerator();
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
stepper.MoveNext();
|
||||
switch (c)
|
||||
{
|
||||
case 'w': w = stepper.Current; break;
|
||||
case 'x': x = stepper.Current; break;
|
||||
case 'y': y = stepper.Current; break;
|
||||
case 'z': z = stepper.Current; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Quaternion Abs(Quaternion num) => new Quaternion(num.Magnitude, 0, 0, 0);
|
||||
public static Quaternion Ceiling(Quaternion num) =>
|
||||
new Quaternion(MathE.Ceiling(num.w),
|
||||
MathE.Ceiling(num.x),
|
||||
MathE.Ceiling(num.y),
|
||||
MathE.Ceiling(num.z));
|
||||
public static Quaternion Ceiling(Quaternion num, Quaternion min, Quaternion max) =>
|
||||
new Quaternion(MathE.Clamp(num.w, min.w, max.w),
|
||||
MathE.Clamp(num.x, min.x, max.x),
|
||||
MathE.Clamp(num.y, min.y, max.y),
|
||||
MathE.Clamp(num.z, min.z, max.z));
|
||||
public static Quaternion ClampMagnitude(Quaternion num, double minMag, double maxMag)
|
||||
{
|
||||
Quaternion copy = num;
|
||||
ClampMagnitude(ref copy, minMag, maxMag);
|
||||
return copy;
|
||||
}
|
||||
public static void ClampMagnitude(ref Quaternion num, double minMag, double maxMag)
|
||||
{
|
||||
if (minMag > maxMag) throw new ClampOrderMismatchException(nameof(minMag), nameof(maxMag));
|
||||
double mag = num.Magnitude;
|
||||
|
||||
double factor;
|
||||
if (mag < minMag) factor = minMag / mag;
|
||||
else if (mag > maxMag) factor = maxMag / mag;
|
||||
else factor = 1;
|
||||
|
||||
num.w *= factor;
|
||||
num.x *= factor;
|
||||
num.y *= factor;
|
||||
num.z *= factor;
|
||||
}
|
||||
public static double Dot(Quaternion a, Quaternion b) => a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
public static double Dot(IEnumerable<Quaternion> nums)
|
||||
{
|
||||
double w = 1, x = 1, y = 1, z = 1;
|
||||
foreach (Quaternion q in nums)
|
||||
{
|
||||
w *= q.w;
|
||||
x *= q.x;
|
||||
y *= q.y;
|
||||
z *= q.z;
|
||||
}
|
||||
return w + x + y + z;
|
||||
}
|
||||
public static Quaternion Floor(Quaternion num) =>
|
||||
new Quaternion(MathE.Floor(num.w),
|
||||
MathE.Floor(num.x),
|
||||
MathE.Floor(num.y),
|
||||
MathE.Floor(num.z));
|
||||
public static Quaternion Lerp(Quaternion a, Quaternion b, double t, bool clamp = true) =>
|
||||
new Quaternion(MathE.Lerp(a.w, b.w, t, clamp),
|
||||
MathE.Lerp(a.x, b.x, t, clamp),
|
||||
MathE.Lerp(a.y, b.y, t, clamp),
|
||||
MathE.Lerp(a.z, b.z, t, clamp));
|
||||
public static Quaternion Product(IEnumerable<Quaternion> nums)
|
||||
{
|
||||
bool any = false;
|
||||
Quaternion result = One;
|
||||
foreach (Quaternion q in nums)
|
||||
{
|
||||
any = true;
|
||||
result *= q;
|
||||
}
|
||||
return any ? result : Zero;
|
||||
}
|
||||
public static Quaternion Round(Quaternion num) =>
|
||||
new Quaternion(MathE.Round(num.w),
|
||||
MathE.Round(num.x),
|
||||
MathE.Round(num.y),
|
||||
MathE.Round(num.z));
|
||||
public static Quaternion Sum(IEnumerable<Quaternion> nums)
|
||||
{
|
||||
bool any = false;
|
||||
Quaternion result = One;
|
||||
foreach (Quaternion q in nums)
|
||||
{
|
||||
any = true;
|
||||
result += q;
|
||||
}
|
||||
return any ? result : Zero;
|
||||
}
|
||||
|
||||
public static (double[] Ws, double[] Xs, double[] Ys, double[] Zs) SplitArray(IEnumerable<Quaternion> nums)
|
||||
{
|
||||
int count = nums.Count();
|
||||
double[] Ws = new double[count],
|
||||
Xs = new double[count],
|
||||
Ys = new double[count],
|
||||
Zs = new double[count];
|
||||
int index = 0;
|
||||
foreach (Quaternion q in nums)
|
||||
{
|
||||
Ws[index] = q.w;
|
||||
Xs[index] = q.x;
|
||||
Ys[index] = q.y;
|
||||
Zs[index] = q.z;
|
||||
index++;
|
||||
}
|
||||
return (Ws, Xs, Ys, Zs);
|
||||
}
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return w;
|
||||
yield return x;
|
||||
yield return y;
|
||||
yield return z;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public int CompareTo(double other) => MagnitudeSqr.CompareTo(other * other);
|
||||
public int CompareTo(Quaternion other) => MagnitudeSqr.CompareTo(other.MagnitudeSqr);
|
||||
#if CS8_OR_GREATER
|
||||
public int CompareTo(object? other)
|
||||
#else
|
||||
public int CompareTo(object other)
|
||||
#endif
|
||||
{
|
||||
if (other is null) return 1;
|
||||
else if (other is Quaternion otherQuat) return CompareTo(otherQuat);
|
||||
else if (other is double otherNum) return CompareTo(otherNum);
|
||||
//else if (TryConvertFrom(other, out Quaternion otherConvert)) return CompareTo(otherConvert);
|
||||
else return 1;
|
||||
}
|
||||
public bool Equals(double other) => w == other && x == 0 && y == 0 && z == 0;
|
||||
public bool Equals(Complex other) => w == other.r && x == other.i && y == 0 && z == 0;
|
||||
public bool Equals(Quaternion other) => w == other.w && x == other.x && y == other.y && z == other.z;
|
||||
#if CS8_OR_GREATER
|
||||
public override bool Equals(object? other)
|
||||
#else
|
||||
public override bool Equals(object other)
|
||||
#endif
|
||||
{
|
||||
if (other is null) return false;
|
||||
else if (other is Quaternion otherQuat) return Equals(otherQuat);
|
||||
else if (other is Complex otherComp) return Equals(otherComp);
|
||||
else if (other is double otherNum) return Equals(otherNum);
|
||||
//else if (TryConvertFrom(other, out Quaternion otherConvert)) return Equals(otherConvert);
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
public override string ToString() => ToStringHelper.HighDimNumberToString(this, null, null);
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format) => ToStringHelper.HighDimNumberToString(this, format, null);
|
||||
public string ToString(IFormatProvider? provider) => ToStringHelper.HighDimNumberToString(this, null, provider);
|
||||
public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.HighDimNumberToString(this, format, provider);
|
||||
#else
|
||||
public string ToString(string format) => ToStringHelper.HighDimNumberToString(this, format, null);
|
||||
public string ToString(IFormatProvider provider) => ToStringHelper.HighDimNumberToString(this, null, provider);
|
||||
public string ToString(string format, IFormatProvider provider) => ToStringHelper.HighDimNumberToString(this, format, provider);
|
||||
#endif
|
||||
|
||||
public double[] ToArray() => new double[] { w, x, y, z };
|
||||
public Fill<double> ToFill()
|
||||
{
|
||||
Quaternion @this = this;
|
||||
return i => @this[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double>() { w, x, y, z };
|
||||
|
||||
public static Quaternion operator +(Quaternion a) => a;
|
||||
public static Quaternion operator +(Quaternion a, Quaternion b) => new Quaternion(a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
public static Quaternion operator +(Quaternion a, Complex b) => new Quaternion(a.w + b.r, a.x + b.i, a.y, a.z);
|
||||
public static Quaternion operator +(Quaternion a, double b) => new Quaternion(a.w + b, a.x, a.y, a.z);
|
||||
public static Quaternion operator -(Quaternion a) => new Quaternion(-a.w, -a.x, -a.y, -a.z);
|
||||
public static Quaternion operator -(Quaternion a, Quaternion b) => new Quaternion(a.w - b.w, a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
public static Quaternion operator -(Quaternion a, Complex b) => new Quaternion(a.w - b.r, a.x - b.i, a.y, a.z);
|
||||
public static Quaternion operator -(Quaternion a, double b) => new Quaternion(a.w - b, a.x, a.y, a.z);
|
||||
public static Quaternion operator *(Quaternion a, Quaternion b) =>
|
||||
new Quaternion(a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
|
||||
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
||||
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
||||
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w);
|
||||
public static Quaternion operator *(Quaternion a, Complex b) =>
|
||||
new Quaternion(a.w * b.r - a.x * b.i,
|
||||
a.x * b.r + a.w * b.i,
|
||||
a.y * b.r + a.z * b.i,
|
||||
a.z * b.r - a.y * b.i);
|
||||
public static Quaternion operator *(Quaternion a, double b) => new Quaternion(a.w * b, a.x * b, a.y * b, a.z * b);
|
||||
public static bool operator ==(Quaternion a, Quaternion b) => a.Equals(b);
|
||||
public static bool operator !=(Quaternion a, Quaternion b) => !a.Equals(b);
|
||||
public static bool operator >(Quaternion a, Quaternion b) => a.CompareTo(b) > 0;
|
||||
public static bool operator <(Quaternion a, Quaternion b) => a.CompareTo(b) < 0;
|
||||
public static bool operator >=(Quaternion a, Quaternion b) => a.CompareTo(b) >= 0;
|
||||
public static bool operator <=(Quaternion a, Quaternion b) => a.CompareTo(b) <= 0;
|
||||
|
||||
public static implicit operator Quaternion(Complex complex) => new Quaternion(complex.r, complex.i, 0, 0);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user