diff --git a/Nerd_STF/Mathematics/Abstract/ICombinationIndexer.cs b/Nerd_STF/Abstract/ICombinationIndexer.cs similarity index 80% rename from Nerd_STF/Mathematics/Abstract/ICombinationIndexer.cs rename to Nerd_STF/Abstract/ICombinationIndexer.cs index 396d5ff..39e5cfe 100644 --- a/Nerd_STF/Mathematics/Abstract/ICombinationIndexer.cs +++ b/Nerd_STF/Abstract/ICombinationIndexer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Nerd_STF.Mathematics.Abstract +namespace Nerd_STF.Abstract { public interface ICombinationIndexer { diff --git a/Nerd_STF/Mathematics/Abstract/IFromTuple.cs b/Nerd_STF/Abstract/IFromTuple.cs similarity index 54% rename from Nerd_STF/Mathematics/Abstract/IFromTuple.cs rename to Nerd_STF/Abstract/IFromTuple.cs index c64d4c2..68803a1 100644 --- a/Nerd_STF/Mathematics/Abstract/IFromTuple.cs +++ b/Nerd_STF/Abstract/IFromTuple.cs @@ -1,14 +1,14 @@ #if CS11_OR_GREATER using System.Runtime.CompilerServices; -namespace Nerd_STF.Mathematics.Abstract +namespace Nerd_STF.Abstract { public interface IFromTuple where TSelf : IFromTuple where TTuple : struct, ITuple { - public static abstract implicit operator TSelf(TTuple tuple); - public static abstract implicit operator TTuple(TSelf tuple); + static abstract implicit operator TSelf(TTuple tuple); + static abstract implicit operator TTuple(TSelf tuple); } } #endif diff --git a/Nerd_STF/Abstract/IInterpolable.cs b/Nerd_STF/Abstract/IInterpolable.cs new file mode 100644 index 0000000..7f22116 --- /dev/null +++ b/Nerd_STF/Abstract/IInterpolable.cs @@ -0,0 +1,10 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Abstract +{ + public interface IInterpolable + where TSelf : IInterpolable + { + static abstract TSelf Lerp(TSelf a, TSelf b, double t, bool clamp = true); + } +} +#endif diff --git a/Nerd_STF/Abstract/IModifiable.cs b/Nerd_STF/Abstract/IModifiable.cs new file mode 100644 index 0000000..caf4781 --- /dev/null +++ b/Nerd_STF/Abstract/IModifiable.cs @@ -0,0 +1,10 @@ +using System; + +namespace Nerd_STF.Abstract +{ + public interface IModifiable + where TSelf : IModifiable + { + void Modify(Action action); + } +} diff --git a/Nerd_STF/Abstract/INumberGroup.cs b/Nerd_STF/Abstract/INumberGroup.cs new file mode 100644 index 0000000..b263029 --- /dev/null +++ b/Nerd_STF/Abstract/INumberGroup.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Nerd_STF.Abstract +{ + public interface INumberGroup : ICombinationIndexer, + IEnumerable, + IEquatable, + IModifiable +#if CS11_OR_GREATER + ,IInterpolable, + ISimpleMathOperations, + IVectorOperations +#endif + where TSelf : INumberGroup +#if CS11_OR_GREATER + where TItem : INumber +#endif + { + TItem this[int index] { get; set; } + + TItem[] ToArray(); + List ToList(); + } +} diff --git a/Nerd_STF/Abstract/IPresets1d.cs b/Nerd_STF/Abstract/IPresets1d.cs new file mode 100644 index 0000000..1fbe0c7 --- /dev/null +++ b/Nerd_STF/Abstract/IPresets1d.cs @@ -0,0 +1,10 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Abstract +{ + public interface IPresets1d where TSelf : IPresets1d + { + static abstract TSelf One { get; } + static abstract TSelf Zero { get; } + } +} +#endif diff --git a/Nerd_STF/Abstract/IPresets2d.cs b/Nerd_STF/Abstract/IPresets2d.cs new file mode 100644 index 0000000..bca7c92 --- /dev/null +++ b/Nerd_STF/Abstract/IPresets2d.cs @@ -0,0 +1,13 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Abstract +{ + public interface IPresets2d : IPresets1d + where TSelf : IPresets2d + { + static abstract TSelf Down { get; } + static abstract TSelf Left { get; } + static abstract TSelf Right { get; } + static abstract TSelf Up { get; } + } +} +#endif diff --git a/Nerd_STF/Abstract/IPresets3d.cs b/Nerd_STF/Abstract/IPresets3d.cs new file mode 100644 index 0000000..e3f4f76 --- /dev/null +++ b/Nerd_STF/Abstract/IPresets3d.cs @@ -0,0 +1,11 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Abstract +{ + public interface IPresets3d : IPresets2d + where TSelf : IPresets3d + { + static abstract TSelf Backward { get; } + static abstract TSelf Forward { get; } + } +} +#endif diff --git a/Nerd_STF/Mathematics/Abstract/IPresets4D.cs b/Nerd_STF/Abstract/IPresets4d.cs similarity index 50% rename from Nerd_STF/Mathematics/Abstract/IPresets4D.cs rename to Nerd_STF/Abstract/IPresets4d.cs index ba967dd..097072b 100644 --- a/Nerd_STF/Mathematics/Abstract/IPresets4D.cs +++ b/Nerd_STF/Abstract/IPresets4d.cs @@ -1,11 +1,11 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Mathematics.Abstract +namespace Nerd_STF.Abstract { public interface IPresets4d : IPresets3d where TSelf : IPresets4d { - public static abstract TSelf LowW { get; } - public static abstract TSelf HighW { get; } + static abstract TSelf LowW { get; } + static abstract TSelf HighW { get; } } } #endif diff --git a/Nerd_STF/Abstract/IRoundable.cs b/Nerd_STF/Abstract/IRoundable.cs new file mode 100644 index 0000000..b9c7fdd --- /dev/null +++ b/Nerd_STF/Abstract/IRoundable.cs @@ -0,0 +1,19 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Abstract +{ + public interface IRoundable : IRoundable + where TSelf : IRoundable { } + + public interface IRoundable + where TSelf : IRoundable + { + static abstract TOut Ceiling(TSelf val); + static abstract TOut Floor(TSelf val); + static abstract TOut Round(TSelf val); + + static abstract void Ceiling(ref TSelf val); + static abstract void Floor(ref TSelf val); + static abstract void Round(ref TSelf val); + } +} +#endif diff --git a/Nerd_STF/Abstract/ISimpleMathOperations.cs b/Nerd_STF/Abstract/ISimpleMathOperations.cs new file mode 100644 index 0000000..3c07e18 --- /dev/null +++ b/Nerd_STF/Abstract/ISimpleMathOperations.cs @@ -0,0 +1,17 @@ +#if CS11_OR_GREATER +using System.Collections.Generic; +using System.Numerics; + +namespace Nerd_STF.Abstract +{ + public interface ISimpleMathOperations : IAdditionOperators, + ISubtractionOperators, + IMultiplyOperators, + IDivisionOperators + where TSelf : ISimpleMathOperations + { + static abstract TSelf Product(IEnumerable vals); + static abstract TSelf Sum(IEnumerable vals); + } +} +#endif diff --git a/Nerd_STF/Mathematics/Abstract/ISplittable.cs b/Nerd_STF/Abstract/ISplittable.cs similarity index 68% rename from Nerd_STF/Mathematics/Abstract/ISplittable.cs rename to Nerd_STF/Abstract/ISplittable.cs index c7de677..4ea4aaf 100644 --- a/Nerd_STF/Mathematics/Abstract/ISplittable.cs +++ b/Nerd_STF/Abstract/ISplittable.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Nerd_STF.Mathematics.Abstract +namespace Nerd_STF.Abstract { public interface ISplittable where TSelf : ISplittable where TTuple : struct, ITuple { - public static abstract TTuple SplitArray(IEnumerable values); + static abstract TTuple SplitArray(IEnumerable values); } } #endif diff --git a/Nerd_STF/Abstract/IVectorOperations.cs b/Nerd_STF/Abstract/IVectorOperations.cs new file mode 100644 index 0000000..2a64f76 --- /dev/null +++ b/Nerd_STF/Abstract/IVectorOperations.cs @@ -0,0 +1,17 @@ +#if CS11_OR_GREATER +using System.Collections.Generic; + +namespace Nerd_STF.Abstract +{ + public interface IVectorOperations : ISimpleMathOperations + where TSelf : IVectorOperations + { + double Magnitude { get; } + + static abstract TSelf ClampMagnitude(TSelf val, double minMag, double maxMag); + static abstract void ClampMagnitude(ref TSelf val, double minMag, double maxMag); + static abstract double Dot(TSelf a, TSelf b); + static abstract double Dot(IEnumerable vals); + } +} +#endif diff --git a/Nerd_STF/Mathematics/Abstract/INumberGroup.cs b/Nerd_STF/Mathematics/Abstract/INumberGroup.cs deleted file mode 100644 index 0f17729..0000000 --- a/Nerd_STF/Mathematics/Abstract/INumberGroup.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Nerd_STF.Mathematics.Abstract -{ - public interface INumberGroup : ICombinationIndexer, - IEnumerable, - IEquatable - where TSelf : INumberGroup - { - TItem this[int index] { get; set; } - - TItem[] ToArray(); - List ToList(); - } -} diff --git a/Nerd_STF/Mathematics/Abstract/IPresets1D.cs b/Nerd_STF/Mathematics/Abstract/IPresets1D.cs deleted file mode 100644 index a58ae85..0000000 --- a/Nerd_STF/Mathematics/Abstract/IPresets1D.cs +++ /dev/null @@ -1,10 +0,0 @@ -#if CS11_OR_GREATER -namespace Nerd_STF.Mathematics.Abstract -{ - public interface IPresets1d where TSelf : IPresets1d - { - public static abstract TSelf One { get; } - public static abstract TSelf Zero { get; } - } -} -#endif diff --git a/Nerd_STF/Mathematics/Abstract/IPresets2D.cs b/Nerd_STF/Mathematics/Abstract/IPresets2D.cs deleted file mode 100644 index 09421bf..0000000 --- a/Nerd_STF/Mathematics/Abstract/IPresets2D.cs +++ /dev/null @@ -1,13 +0,0 @@ -#if CS11_OR_GREATER -namespace Nerd_STF.Mathematics.Abstract -{ - public interface IPresets2d : IPresets1d - where TSelf : IPresets2d - { - public static abstract TSelf Down { get; } - public static abstract TSelf Left { get; } - public static abstract TSelf Right { get; } - public static abstract TSelf Up { get; } - } -} -#endif diff --git a/Nerd_STF/Mathematics/Abstract/IPresets3D.cs b/Nerd_STF/Mathematics/Abstract/IPresets3D.cs deleted file mode 100644 index 81048e2..0000000 --- a/Nerd_STF/Mathematics/Abstract/IPresets3D.cs +++ /dev/null @@ -1,11 +0,0 @@ -#if CS11_OR_GREATER -namespace Nerd_STF.Mathematics.Abstract -{ - public interface IPresets3d : IPresets2d - where TSelf : IPresets3d - { - public static abstract TSelf Backward { get; } - public static abstract TSelf Forward { get; } - } -} -#endif diff --git a/Nerd_STF/Mathematics/Angle.cs b/Nerd_STF/Mathematics/Angle.cs new file mode 100644 index 0000000..1cda956 --- /dev/null +++ b/Nerd_STF/Mathematics/Angle.cs @@ -0,0 +1,253 @@ +using Nerd_STF.Abstract; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nerd_STF.Mathematics +{ + public struct Angle : IComparable, + IEquatable, + IModifiable +#if CS11_OR_GREATER + ,IPresets2d, + IFromTuple +#endif + { + public static Angle Down => new Angle(0.75); + public static Angle Left => new Angle(0.5); + public static Angle Right => new Angle(0); + public static Angle Up => new Angle(0.25); + + public static Angle Full => new Angle(1); + public static Angle Half => new Angle(0.5); + public static Angle Quarter => new Angle(0.25); + public static Angle Zero => new Angle(0); + +#if CS11_OR_GREATER + static Angle IPresets1d.One => new Angle(1, Unit.Degrees); +#endif + + public double Degrees + { + get => revTheta * 360; + set => revTheta = value / 360; + } + public double Gradians + { + get => revTheta * 400; + set => revTheta = value / 400; + } + public double Radians + { + get => revTheta * Constants.Tau; + set => revTheta = value / Constants.Tau; + } + public double Revolutions + { + get => revTheta; + set => revTheta = value; + } + + public Angle Complimentary => new Angle(0.25 - MathE.AbsoluteMod(revTheta, 1)); + public Angle Supplimentary => new Angle(0.5 - MathE.AbsoluteMod(revTheta, 1)); + public Angle Normalized => new Angle(MathE.AbsoluteMod(revTheta, 1)); + public Angle Reflected => new Angle(MathE.AbsoluteMod(-revTheta, 1)); + + private double revTheta; + + public Angle(double theta, Unit unit) + { + switch (unit) + { + case Unit.Revolutions: revTheta = theta; break; + case Unit.Degrees: revTheta = theta / 360; break; + case Unit.Radians: revTheta = theta / Constants.Tau; break; + case Unit.Gradians: revTheta = theta / 400; break; + default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); + } + } + private Angle(double revTheta) + { + this.revTheta = revTheta; + } + + public double this[Unit unit] + { + get + { + switch (unit) + { + case Unit.Revolutions: return revTheta; + case Unit.Degrees: return revTheta * 360; + case Unit.Radians: return revTheta * Constants.Tau; + case Unit.Gradians: return revTheta * 400; + default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); + } + } + set + { + switch (unit) + { + case Unit.Revolutions: revTheta = value; break; + case Unit.Degrees: revTheta = value / 360; break; + case Unit.Radians: revTheta = value / Constants.Tau; break; + case Unit.Gradians: revTheta = value / 400; break; + default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); + } + } + } + + public static Angle Average(IEnumerable angles) + { + Angle sum = Zero; + int count = 0; + foreach (Angle ang in angles) + { + sum += ang; + count++; + } + return sum; + } + public static Angle Clamp(Angle value, Angle min, Angle max) => + new Angle(MathE.Clamp(value.revTheta, min.revTheta, max.revTheta)); + public static void Clamp(ref Angle value, Angle min, Angle max) => + MathE.Clamp(ref value.revTheta, min.revTheta, max.revTheta); + public static Angle Max(IEnumerable values) => Max(false, values); + public static Angle Max(bool normalize, IEnumerable values) + { + bool any = false; + Angle best = Zero; + double bestNormalized = 0; + foreach (Angle ang in values) + { + if (!any) + { + best = ang; + if (normalize) bestNormalized = MathE.AbsoluteMod(ang.revTheta, 1); + any = true; + } + else if (normalize) + { + double angNormalized = MathE.AbsoluteMod(ang.revTheta, 1); + if (angNormalized > bestNormalized) + { + best = ang; + bestNormalized = angNormalized; + } + } + else if (ang.revTheta > best.revTheta) best = ang; + } + return best; + } + public static Angle Min(IEnumerable values) => Min(false, values); + public static Angle Min(bool normalize, IEnumerable values) + { + bool any = false; + Angle best = Zero; + double bestNormalized = 0; + foreach (Angle ang in values) + { + if (!any) + { + best = ang; + if (normalize) bestNormalized = MathE.AbsoluteMod(ang.revTheta, 1); + any = true; + } + else if (normalize) + { + double angNormalized = MathE.AbsoluteMod(ang.revTheta, 1); + if (angNormalized < bestNormalized) + { + best = ang; + bestNormalized = angNormalized; + } + } + else if (ang.revTheta < best.revTheta) best = ang; + } + return best; + } + public static Angle Sum(IEnumerable angles) + { + Angle sum = Zero; + foreach (Angle ang in angles) sum += ang; + return sum; + } + + public static double[] SplitArray(Unit unit, IEnumerable values) + { + int count = values.Count(); + double[] angles = new double[count]; + int index = 0; + foreach (Angle val in values) + { + angles[index] = val[unit]; + index++; + } + return angles; + } + + public void Modify(Action action) => action(this); + + public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta); + public bool Equals(Angle other) => revTheta == other.revTheta; +#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 Angle otherAng) return Equals(otherAng); + else return false; + } + public override int GetHashCode() => revTheta.GetHashCode(); + public override string ToString() => ToString(Unit.Degrees, null); + public string ToString(Unit unit) => ToString(unit, null); +#if CS8_OR_GREATER + public string ToString(string? format) => +#else + public string ToString(string format) => +#endif + ToString(Unit.Degrees, format); +#if CS8_OR_GREATER + public string ToString(Unit unit, string? format) +#else + public string ToString(Unit unit, string format) +#endif + { + switch (unit) + { + case Unit.Revolutions: return $"{revTheta.ToString(format)} rev"; + case Unit.Degrees: return $"{(revTheta * 360).ToString(format)} deg"; + case Unit.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad"; + case Unit.Gradians: return $"{(revTheta * 400).ToString(format)} grad"; + default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); + } + } + + public static Angle operator +(Angle a, Angle b) => new Angle(a.revTheta + b.revTheta); + public static Angle operator -(Angle a) => new Angle(-a.revTheta); + public static Angle operator -(Angle a, Angle b) => new Angle(a.revTheta - b.revTheta); + public static Angle operator *(Angle a, double b) => new Angle(a.revTheta * b); + public static Angle operator /(Angle a, double b) => new Angle(a.revTheta / b); + public static bool operator ==(Angle a, Angle b) => a.Equals(b); + public static bool operator !=(Angle a, Angle b) => !a.Equals(b); + public static bool operator >(Angle a, Angle b) => a.CompareTo(b) > 0; + public static bool operator <(Angle a, Angle b) => a.CompareTo(b) < 0; + public static bool operator >=(Angle a, Angle b) => a.CompareTo(b) >= 0; + public static bool operator <=(Angle a, Angle b) => a.CompareTo(b) <= 0; + + public static implicit operator Angle((double, Unit) tuple) => new Angle(tuple.Item1, tuple.Item2); +#if CS11_OR_GREATER + static implicit IFromTuple.operator ValueTuple(Angle angle) => (angle.revTheta, Unit.Revolutions); +#endif + + public enum Unit + { + Revolutions, + Degrees, + Radians, + Gradians + } + } +} diff --git a/Nerd_STF/Mathematics/Equations/IEquation.cs b/Nerd_STF/Mathematics/Equations/IEquation.cs index 2e9a856..7e57145 100644 --- a/Nerd_STF/Mathematics/Equations/IEquation.cs +++ b/Nerd_STF/Mathematics/Equations/IEquation.cs @@ -23,5 +23,17 @@ namespace Nerd_STF.Mathematics.Equations double Integrate(double lower, double upper); // TODO: Solve + +#if CS8_OR_GREATER + static IEquation operator +(IEquation a, IEquation b) => a.Add(b); + static IEquation operator +(IEquation a, double b) => a.Add(b); + static IEquation operator -(IEquation a) => a.Negate(); + static IEquation operator -(IEquation a, IEquation b) => a.Subtract(b); + static IEquation operator -(IEquation a, double b) => a.Subtract(b); + static IEquation operator *(IEquation a, IEquation b) => a.Multiply(b); + static IEquation operator *(IEquation a, double b) => a.Multiply(b); + static IEquation operator /(IEquation a, IEquation b) => a.Divide(b); + static IEquation operator /(IEquation a, double b) => a.Divide(b); +#endif } } diff --git a/Nerd_STF/Mathematics/Equations/Polynomial.cs b/Nerd_STF/Mathematics/Equations/Polynomial.cs index b1b52dc..cc5017d 100644 --- a/Nerd_STF/Mathematics/Equations/Polynomial.cs +++ b/Nerd_STF/Mathematics/Equations/Polynomial.cs @@ -79,6 +79,8 @@ namespace Nerd_STF.Mathematics.Equations public IEquation Add(IEquation other) { if (other is Polynomial otherPoly) return Add(otherPoly); + else if (other is Quadratic otherQuad) return Add((Polynomial)otherQuad); + else if (other is Linear otherLinear) return Add((Polynomial)otherLinear); else return new Equation((double x) => Get(x) + other.Get(x)); } public Polynomial Add(double constant) diff --git a/Nerd_STF/Mathematics/Equations/Quadratic.cs b/Nerd_STF/Mathematics/Equations/Quadratic.cs index 3c68bc0..c7ab443 100644 --- a/Nerd_STF/Mathematics/Equations/Quadratic.cs +++ b/Nerd_STF/Mathematics/Equations/Quadratic.cs @@ -183,6 +183,7 @@ namespace Nerd_STF.Mathematics.Equations public static Polynomial operator *(Quadratic a, double b) => a.Multiply(b); public static IEquation operator /(Quadratic a, IEquation b) => a.Divide(b); public static Quadratic operator /(Quadratic a, double b) => a.Divide(b); + public static bool operator ==(Quadratic a, Quadratic b) => a.Equals(b); public static bool operator ==(Quadratic a, Polynomial b) => a.Equals(b); public static bool operator !=(Quadratic a, Quadratic b) => !a.Equals(b); diff --git a/Nerd_STF/Mathematics/Float2.cs b/Nerd_STF/Mathematics/Float2.cs index d2aab54..3ee6cf2 100644 --- a/Nerd_STF/Mathematics/Float2.cs +++ b/Nerd_STF/Mathematics/Float2.cs @@ -1,9 +1,8 @@ -using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; +using Nerd_STF.Abstract; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Drawing; using System.Linq; @@ -13,6 +12,7 @@ namespace Nerd_STF.Mathematics #if CS11_OR_GREATER ,IFromTuple, IPresets2d, + IRoundable, ISplittable #endif { @@ -236,6 +236,8 @@ namespace Nerd_STF.Mathematics y = this.y; } + public void Modify(Action action) => action(this); + public bool Equals(Float2 other) => x == other.x && y == other.y; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/Float3.cs b/Nerd_STF/Mathematics/Float3.cs index 2ee9507..aaa29f1 100644 --- a/Nerd_STF/Mathematics/Float3.cs +++ b/Nerd_STF/Mathematics/Float3.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; +using Nerd_STF.Abstract; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -11,6 +11,7 @@ namespace Nerd_STF.Mathematics #if CS11_OR_GREATER ,IFromTuple, IPresets2d, + IRoundable, ISplittable #endif { @@ -261,6 +262,8 @@ namespace Nerd_STF.Mathematics z = this.z; } + public void Modify(Action action) => action(this); + public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/Float4.cs b/Nerd_STF/Mathematics/Float4.cs index 3611933..2d3ac7a 100644 --- a/Nerd_STF/Mathematics/Float4.cs +++ b/Nerd_STF/Mathematics/Float4.cs @@ -2,8 +2,8 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using Nerd_STF.Abstract; using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; namespace Nerd_STF.Mathematics { @@ -11,6 +11,7 @@ namespace Nerd_STF.Mathematics #if CS11_OR_GREATER ,IFromTuple, IPresets4d, + IRoundable, ISplittable #endif { @@ -281,6 +282,8 @@ namespace Nerd_STF.Mathematics z = this.z; } + public void Modify(Action action) => action(this); + public bool Equals(Float4 other) => w == other.w && x == other.x && y == other.y && z == other.z; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/Int2.cs b/Nerd_STF/Mathematics/Int2.cs index 8585d81..d6356e5 100644 --- a/Nerd_STF/Mathematics/Int2.cs +++ b/Nerd_STF/Mathematics/Int2.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; +using Nerd_STF.Abstract; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -120,27 +120,27 @@ namespace Nerd_STF.Mathematics MathE.Clamp(ref value.x, min.x, max.x); MathE.Clamp(ref value.y, min.y, max.y); } - public static Int2 ClampMagnitude(Int2 value, int minMag, int maxMag) + public static Int2 ClampMagnitude(Int2 value, double minMag, double maxMag) { Int2 copy = value; ClampMagnitude(ref copy, minMag, maxMag); return copy; } - public static void ClampMagnitude(ref Int2 value, int minMag, int maxMag) + public static void ClampMagnitude(ref Int2 value, double minMag, double maxMag) { if (minMag > maxMag) throw new ClampOrderMismatchException(nameof(minMag), nameof(maxMag)); double mag = value.Magnitude; if (mag < minMag) { double factor = minMag / mag; - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); + value.x = MathE.Ceiling(value.x * factor); + value.y = MathE.Ceiling(value.y * factor); } else if (mag > maxMag) { double factor = maxMag / mag; - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); + value.x = MathE.Floor(value.x * factor); + value.y = MathE.Floor(value.y * factor); } } public static Int3 Cross(Int2 a, Int2 b) => Int3.Cross(a, b); @@ -155,6 +155,10 @@ namespace Nerd_STF.Mathematics } return x + y; } +#if CS11_OR_GREATER + static double IVectorOperations.Dot(Int2 a, Int2 b) => Dot(a, b); + static double IVectorOperations.Dot(IEnumerable vals) => Dot(vals); +#endif public static Int2 Lerp(Int2 a, Int2 b, double t, bool clamp = true) => new Int2(MathE.Lerp(a.x, b.x, t, clamp), MathE.Lerp(a.y, b.y, t, clamp)); @@ -203,6 +207,8 @@ namespace Nerd_STF.Mathematics y = this.y; } + public void Modify(Action action) => action(this); + public bool Equals(Int2 other) => x == other.x && y == other.y; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/Int3.cs b/Nerd_STF/Mathematics/Int3.cs index 547cd53..beb20a9 100644 --- a/Nerd_STF/Mathematics/Int3.cs +++ b/Nerd_STF/Mathematics/Int3.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; +using Nerd_STF.Abstract; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -129,13 +129,13 @@ namespace Nerd_STF.Mathematics MathE.Clamp(ref value.y, min.y, max.y); MathE.Clamp(ref value.z, min.z, max.z); } - public static Int3 ClampMagnitude(Int3 value, int minMag, int maxMag) + public static Int3 ClampMagnitude(Int3 value, double minMag, double maxMag) { Int3 copy = value; ClampMagnitude(ref copy, minMag, maxMag); return copy; } - public static void ClampMagnitude(ref Int3 value, int minMag, int maxMag) + public static void ClampMagnitude(ref Int3 value, double minMag, double maxMag) { if (minMag > maxMag) throw new ClampOrderMismatchException(nameof(minMag), nameof(maxMag)); double mag = value.Magnitude; @@ -143,16 +143,16 @@ namespace Nerd_STF.Mathematics if (mag < minMag) { double factor = minMag / mag; - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); - value.z = (int)(value.z * factor); + value.x = MathE.Ceiling(value.x * factor); + value.y = MathE.Ceiling(value.y * factor); + value.z = MathE.Ceiling(value.z * factor); } else if (mag > maxMag) { double factor = maxMag / mag; - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); - value.z = (int)(value.z * factor); + value.x = MathE.Floor(value.x * factor); + value.y = MathE.Floor(value.y * factor); + value.z = MathE.Floor(value.z * factor); } } public static Int3 Cross(Int3 a, Int3 b) => @@ -171,6 +171,10 @@ namespace Nerd_STF.Mathematics } return x + y + z; } +#if CS11_OR_GREATER + static double IVectorOperations.Dot(Int3 a, Int3 b) => Dot(a, b); + static double IVectorOperations.Dot(IEnumerable vals) => Dot(vals); +#endif public static Int3 Lerp(Int3 a, Int3 b, double t, bool clamp = true) => new Int3(MathE.Lerp(a.x, b.x, t, clamp), MathE.Lerp(a.y, b.y, t, clamp), @@ -223,6 +227,8 @@ namespace Nerd_STF.Mathematics z = this.z; } + public void Modify(Action action) => action(this); + public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/Int4.cs b/Nerd_STF/Mathematics/Int4.cs index c1bee2a..e1ab017 100644 --- a/Nerd_STF/Mathematics/Int4.cs +++ b/Nerd_STF/Mathematics/Int4.cs @@ -1,9 +1,9 @@ -using System; +using Nerd_STF.Abstract; +using Nerd_STF.Exceptions; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using Nerd_STF.Exceptions; -using Nerd_STF.Mathematics.Abstract; namespace Nerd_STF.Mathematics { @@ -152,18 +152,18 @@ namespace Nerd_STF.Mathematics if (mag < minMag) { double factor = minMag / mag; - value.w = (int)(value.w * factor); - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); - value.z = (int)(value.z * factor); + value.w = MathE.Ceiling(value.w * factor); + value.x = MathE.Ceiling(value.x * factor); + value.y = MathE.Ceiling(value.y * factor); + value.z = MathE.Ceiling(value.z * factor); } else if (mag > maxMag) { double factor = maxMag / mag; - value.w = (int)(value.w * factor); - value.x = (int)(value.x * factor); - value.y = (int)(value.y * factor); - value.z = (int)(value.z * factor); + value.w = MathE.Floor(value.w * factor); + value.x = MathE.Floor(value.x * factor); + value.y = MathE.Floor(value.y * factor); + value.z = MathE.Floor(value.z * factor); } } public static int Dot(Int4 a, Int4 b) => a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z; @@ -179,6 +179,10 @@ namespace Nerd_STF.Mathematics } return w + x + y + z; } +#if CS11_OR_GREATER + static double IVectorOperations.Dot(Int4 a, Int4 b) => Dot(a, b); + static double IVectorOperations.Dot(IEnumerable vals) => Dot(vals); +#endif public static Int4 Lerp(Int4 a, Int4 b, double t, bool clamp = true) => new Int4(MathE.Lerp(a.w, b.w, t, clamp), MathE.Lerp(a.x, b.x, t, clamp), @@ -235,6 +239,8 @@ namespace Nerd_STF.Mathematics z = this.z; } + public void Modify(Action action) => action(this); + public bool Equals(Int4 other) => w == other.w && x == other.x && y == other.y && z == other.z; #if CS8_OR_GREATER public override bool Equals(object? obj) diff --git a/Nerd_STF/Mathematics/MathE.cs b/Nerd_STF/Mathematics/MathE.cs index 318ecea..345e1a6 100644 --- a/Nerd_STF/Mathematics/MathE.cs +++ b/Nerd_STF/Mathematics/MathE.cs @@ -435,7 +435,7 @@ namespace Nerd_STF.Mathematics } else if (val > best) best = val; } - return any ? best : 0; + return best; } public static double Max(IEnumerable values) { @@ -488,7 +488,7 @@ namespace Nerd_STF.Mathematics } else if (val < best) best = val; } - return any ? best : 0; + return best; } public static double Min(IEnumerable values) { diff --git a/Nerd_STF/Mathematics/Numbers/Fraction.cs b/Nerd_STF/Mathematics/Numbers/Fraction.cs new file mode 100644 index 0000000..c4b4701 --- /dev/null +++ b/Nerd_STF/Mathematics/Numbers/Fraction.cs @@ -0,0 +1,70 @@ +using System.Numerics; + +namespace Nerd_STF.Mathematics.Numbers +{ + public struct Fraction + { + public static Fraction One => new Fraction(1, 1); + public static Fraction Zero => new Fraction(0, 1); + + public int numerator; + public int denominator; + + public Fraction(int numerator, int denominator) + { + this.numerator = numerator; + this.denominator = denominator; + } + + public static Fraction Approximate(double number, int iterations = 32) + { + // Forget what this algorithm is called. When I remember, I'll put its + // Wikipedia page here. + + if (number == 0) return Zero; + else if (number == 1) return One; + else if (number < 0) + { + Fraction result = Approximate(-number, iterations); + result.numerator = -result.numerator; + return result; + } + else if (number > 1) + { + int whole = (int)number; + Fraction result = Approximate(number % 1, iterations); + result.numerator += whole * result.denominator; + return result; + } + + int minNum = 0, maxNum = 1, newNum = minNum + maxNum, + minDen = 1, maxDen = 1, newDen = minDen + maxDen; + double newVal = (double)newNum / newDen; + for (int i = 0; i < iterations; i++) + { + if (number == newVal) break; + else if (number > newVal) + { + minNum = newNum; + minDen = newDen; + } + else // if (number < newVal) + { + maxNum = newNum; + maxDen = newDen; + } + newNum = minNum + maxNum; + newDen = minDen + maxDen; + newVal = (double)newNum / newDen; + } + return new Fraction(newNum, newDen); + } + + public double GetValue() => (double)numerator / denominator; + + public override string ToString() => $"{numerator} / {denominator}"; + + public static implicit operator double(Fraction frac) => frac.GetValue(); + public static explicit operator Fraction(double num) => Approximate(num); + } +} diff --git a/Nerd_STF/Nerd_STF.csproj b/Nerd_STF/Nerd_STF.csproj index bebd223..a22f8b4 100644 --- a/Nerd_STF/Nerd_STF.csproj +++ b/Nerd_STF/Nerd_STF.csproj @@ -7,7 +7,7 @@ embedded True Nerd_STF - 3.0.0-beta1 + 3.0.0-beta2 That_One_Nerd A general-purpose mathematics library for C#. https://github.com/That-One-Nerd/Nerd_STF