Releasing alpha v2.3.1.39 #13

Merged
That-One-Nerd merged 1 commits from main into v2.3 2022-09-30 17:09:00 -04:00
22 changed files with 456 additions and 412 deletions

View File

@ -1,173 +1,218 @@
# Nerd_STF v2.3.0
# Nerd_STF v2.3.1.39
This update adds lots of linear algebra tools, like matrixes and vectors, as well as new number systems, like complex numbers and quaternions.
***Read this to know what works and what doesn't!***
The `v2.3.1.x` updates go through every single field and method in Nerd_STF to make sure it works correctly.
You see, up until now I haven't actually tested literally anything at all. Partly because I didn't have the tools to and partly because I was lazy. But now, it's guarenteed to work in most cases (unless I like don't pick up some bug, you know).
The following types have been checked for the most part and can be safe to use:
- `Nerd_STF.FileType`
- `Nerd_STF.Fill`
- `Nerd_STF.Fill2D`
- `Nerd_STF.Foreach`
- `Nerd_STF.IClosest`
- `Nerd_STF.IContainer`
- `Nerd_STF.IEncapsulator`
- `Nerd_STF.IGroup`
- `Nerd_STF.IGroup2D`
- `Nerd_STF.Logger`
- `Nerd_STF.LogMessage`
- `Nerd_STF.LogSeverity`
- `Nerd_STF.Modifier`
- `Nerd_STF.Modifier2D`
- `Nerd_STF.Exceptions.DifferingVertCountException`
- `Nerd_STF.Exceptions.DisconnectedLinesException`
- `Nerd_STF.Exceptions.InvalidSizeException`
- `Nerd_STF.Exceptions.Nerd_STFException`
- `Nerd_STF.Exceptions.NoInverseException`
- `Nerd_STF.Extensions.ToFillExtension`
- `Nerd_STF.Graphics.ColorChannel`
- `Nerd_STF.Graphics.IlluminationFlags`
- `Nerd_STF.Graphics.IlluminationModel`
- `Nerd_STF.Graphics.Material`
- `Nerd_STF.Graphics.TextureConfig`
- `Nerd_STF.Mathematics.Angle`
- `Nerd_STF.Mathematics.Calculus`
- `Nerd_STF.Mathematics.Equation`
- `Nerd_STF.Mathematics.Float2`
- `Nerd_STF.Mathematics.Float3`
- `Nerd_STF.Mathematics.Float4`
- `Nerd_STF.Mathematics.Int2`
- `Nerd_STF.Mathematics.Int3`
- `Nerd_STF.Mathematics.Int4`
- `Nerd_STF.Mathematics.Mathf`
- `Nerd_STF.Mathematics.NumberSystems.Complex`
- `Nerd_STF.Mathematics.NumberSystems.Quaternion`
- With the exception of the following methods:
- `Quaternion.FromAngles(Angle, Angle, Angle?)`
- `Quaternion.FromAngles(Float3, Angle.Type)`
- `Quaternion.FromVector(Vector3d)`
- `GetAngle()`
- `GetAxis()`
- `ToAngles()`
- `ToVector()`
- `Nerd_STF.Mathematics.Samples.Constants`
- `Nerd_STF.Mathematics.Samples.Equations`
The following types haven't been checked yet, and should still be taken with a grain of salt:
- `Nerd_STF.Extensions.Container2DExtension`
- `Nerd_STF.Extensions.ConversionExtension`
- `Nerd_STF.Extensions.EquationExtension`
- `Nerd_STF.Graphics.CMYKA`
- `Nerd_STF.Graphics.CMYKAByte`
- `Nerd_STF.Graphics.HSVA`
- `Nerd_STF.Graphics.HSVAByte`
- `Nerd_STF.Graphics.IColor`
- `Nerd_STF.Graphics.IColorByte`
- `Nerd_STF.Graphics.Image`
- `Nerd_STF.Graphics.RGBA`
- `Nerd_STF.Graphics.RGBAByte`
- `Nerd_STF.Mathematics.Algebra.IMatrix`
- `Nerd_STF.Mathematics.Algebra.Matrix`
- `Nerd_STF.Mathematics.Algebra.Matrix2x2`
- `Nerd_STF.Mathematics.Algebra.Matrix3x3`
- `Nerd_STF.Mathematics.Algebra.Matrix4x4`
- `Nerd_STF.Mathematics.Algebra.Vector2d`
- `Nerd_STF.Mathematics.Algebra.Vector3d`
- `Nerd_STF.Mathematics.Geometry.Box2D`
- `Nerd_STF.Mathematics.Geometry.Box3D`
- `Nerd_STF.Mathematics.Geometry.ISubdividable`
- `Nerd_STF.Mathematics.Geometry.ITriangulatable`
- `Nerd_STF.Mathematics.Geometry.Line`
- `Nerd_STF.Mathematics.Geometry.Polygon`
- `Nerd_STF.Mathematics.Geometry.Quadrilateral`
- `Nerd_STF.Mathematics.Geometry.Sphere`
- `Nerd_STF.Mathematics.Geometry.Triangle`
- `Nerd_STF.Mathematics.Geometry.Vert`
I've checked a total of 39 types, with 29 left to go. It sounds like I'm over halfway, but really I've saved all of the annoying ones that are likely wrong to be checked next. My 39 done includes a bunch of really simple types that never needed checking in the first place too. But there are some done, like any type in the Mathematics directory.
```
* Nerd_STF
+ Extensions
+ ConversionExtension
+ Container2DExtension
+ ToFillExtension
+ Foreach
+ IGroup2D
+ Modifier
+ Modifier2D
* IGroup<T>
+ ToFill()
* Exceptions
+ InvalidSizeException
+ NoInverseException
* Graphics
* CMYKA
+ ToFill()
+ static Round(CMYKA)
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* CMYKAByte
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* HSVA
+ ToFill()
+ static Round(HSVA, Angle.Type)
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* HSVAByte
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Image
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Material
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Merged 2 if statements into 1 in `override bool Equals(object?)`
* RGBA
+ ToFill()
+ ToVector()
+ static Round(RGBA)
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* RGBAByte
+ ToFill()
+ ToVector()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ MathException
+ UndefinedException
* Extensions
+ EquationExtension
* Mathematics
+ Algebra
+ IMatrix
+ Matrix2x2
+ Matrix3x3
+ Matrix4x4
+ Vector2d
+ Vector3d
+ NumberSystems
+ Complex
+ Quaternion
+ Samples
+ Equations
+ ScaleType
= Moved `Constants` file to Samples folder.
* Geometry
* Box2D
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Box3D
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Line
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Polygon
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Quadrilateral
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Sphere
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Triangle
+ ToFill()
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* Vert
+ ToFill()
+ ToVector()
= Made `Vert(Float2)` not recreate a float group, and instead use itself.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
* NumberSystems
* Complex
+ Inverse
+ GetAngle()
= Fixed `ToString()` and all overloads from adding a redundant negative sign sometimes in the imaginary component.
= Replaced the `/` operator with a multiplication by its inverse.
* Quaternion
+ Inverse
= Fixed `ToString()` and all overloads from adding a redundant negative sign sometimes in the i, j, and k components.
= Replaced the `/` operator with a multiplication by its inverse.
* Algebra
* Vector2d
- static Product(params Vector2d[])
- static Divide(Vector2d, params Vector2d[])
- operator *(Vector2d, Angle)
- operator *(Vector2d, Vector2d)
- operator /(Vector2d, Angle)
- operator /(Vector2d, Vector2d)
= Made `ToXYZ()` actually work
* Vector3d
- static Product(params Vector3d[])
- static Divide(Vector3d, params Vector3d[])
- operator *(Vector3d, Angle)
- operator *(Vector3d, Vector3d)
- operator /(Vector3d, Angle)
- operator /(Vector3d, Vector3d)
* Calculus
+ GetDynamicIntegral(Equation, Equation, Equation, float)
= Made `GetDerivative(Equation, float)` calculate specific values on the fly, and removed the min/max.
* Graphics
* HSVA
- static LerpSquared(HSVA, HSVA, float, Angle.Type, bool = true)
- operator *(HSVA, HSVA)
- operator /(HSVA, HSVA)
* HSVAByte
- static LerpSquared(HSVAByte, HSVAByte, float, Angle.Type, bool = true)
* Angle
+ static Down
+ static Left
+ static Right
+ static Up
+ static Round(Angle, Type)
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ Reflected
+ static Max(bool, params Angle[])
+ static Min(bool, params Angle[])
- operator *(Angle, Angle)
- operator /(Angle, Angle)
= Made `Angle(float, Type)` use a lambda expression
= Made `Bounded` use the `AbsoluteMod()` function instead of the default `%` expression
= Replaced an accidental `Mathf.Floor()` call with a `Mathf.Round()` call in `Floor()`
= Made `operator -(Angle)` actually give you the reverse angle (not the reflected angle)
* Float2
+ ToFill()
+ ToVector()
+ static Round(Float2)
+ implicit operator Float2(Complex)
+ explicit operator Float2(Quaternion)
+ explicit operator Float2(Matrix)
+ operator *(Float2, Matrix)
+ operator /(Float2, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Made `Median(params Float2)` not use an unneccesary average
= Made `ToVector()` not create an angle out of `ArcTan(float)`, as it already is one
= Optimized `Divide(Float2, params Float2[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float2, params Float2[])` to subtract a sum instead of individually subtracting
* Float3
+ ToFill()
+ ToVector()
+ static Round(Float3)
+ implicit operator Float3(Complex)
+ explicit operator Float3(Quaternion)
+ explicit operator Float3(Matrix)
+ operator *(Float3, Matrix)
+ operator /(Float3, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Made `Median(params Float3)` not use an unneccesary average
= Made `ToVector()` not create an angle out of the arc trig functions, as they already are angles
= Optimized `Divide(Float3, params Float3[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float3, params Float3[])` to subtract a sum instead of individually subtracting
* Float4
+ ToFill()
+ static Round(Float4)
+ implicit operator Float4(Complex)
+ implicit operator Float4(Quaternion)
+ explicit operator Float4(Matrix)
+ operator *(Float4, Matrix)
+ operator /(Float4, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `Float4.Deep` to `Float4.Near`
= Marked `Far` and `Near` as obsolete/deprecated, as they have been replaced by `HighW` and `LowW`
= Made `Median(params Float4)` not use an unneccesary average
= Optimized `Divide(Float4, params Float4[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float4, params Float4[])` to subtract a sum instead of individually subtracting
* Int2
+ ToFill()
+ explicit operator Int2(Complex)
+ explicit operator Int2(Quaternion)
+ explicit operator Int2(Matrix)
+ operator *(Int2, Matrix)
+ operator /(Int2, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Made `Median(params Float4)` not use an unneccesary average
= Optimized `Divide(Float4, params Float4[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float4, params Float4[])` to subtract a sum instead of individually subtracting
* Int3
+ ToFill()
+ explicit operator Int3(Complex)
+ explicit operator Int3(Quaternion)
+ explicit operator Int3(Matrix)
+ operator *(Int3, Matrix)
+ operator /(Int3, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Made `Median(params Float4)` not use an unneccesary average
= Optimized `Divide(Float4, params Float4[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float4, params Float4[])` to subtract a sum instead of individually subtracting
* Int4
+ explicit operator Int4(Complex)
+ explicit operator Int4(Quaternion)
+ explicit operator Int4(Matrix)
+ operator *(Int4, Matrix)
+ operator /(Int4, Matrix)
= Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
= Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ LowW
= Marked `Far` as obsolete/deprecated, as it has been replaced by `HighW`
= Made `Median(params Float4)` not use an unneccesary average
= Optimized `Divide(Float4, params Float4[])` to divide a product instead of individually dividing
= Optimized `Subtract(Float4, params Float4[])` to subtract a sum instead of individually subtracting
* Mathf
+ Cos(Angle)
+ Cot(Angle)
+ Csc(Angle)
+ Dot(float[], float[])
+ Dot(float[][])
+ Max<T>(T[]) where T : IComparable<T>
+ Median<T>(T[])
+ Min<T>(T[]) where T : IComparable<T>
+ Sec(Angle)
+ Sin(Angle)
+ Tan(Angle)
* Miscellaneous
* GlobalUsings.cs
+ global using Nerd_STF.Collections
+ global using Nerd_STF.Extensions
+ global using Nerd_STF.Mathematics.Algebra
+ global using Nerd_STF.Mathematics.NumberSystems
+ global using Nerd_STF.Mathematics.Samples
+ AbsoluteMod(float)
+ Binomial(int, int, float)
+ Factors(int)
+ PowerMod(int, int, int)
= Fixed `Ceiling(float)` from rounding up when it shouldn't
= Made `Median(params float[])` actually calculate the right midpoint
= Removed two unneeded `Average(float, float)` calls in `Median(params float[])`
= Replaced the `Floor(float)` and `Ceiling(float)` calls in `Median(params float[])` with a better alternative
= Replaced the mod in `Sin(float)` with an `AbsoluteMod(float, float)` call
= Made `MadeEquation(Dictionary<float, float>)` actually work.
= Added caching to Power(float, int) for the absolute max
= Fixed an if statement and added another one to prevent extra computation in `Power(float, int)`
= Added caching to Power(int, int) for the absolute max
= Fixed an if statement and added another one to prevent extra computation in `Power(int, int)`
= Made the `Lerp(int, int, float, bool)` function not call itself infinitely many times.
= Replaced a multiplication with a division in `Root(float, float)`
= Made the `Average(int[])` function give you the int average instead of the float average (unlike `Average(float[])`)
= Replaced the float return statement with an Angle in `ArcCos(float)`
= Replaced the float return statement with an Angle in `ArcCot(float)`
= Replaced the float return statement with an Angle in `ArcCsc(float)`
= Replaced the float return statement with an Angle in `ArcSec(float)`
= Replaced the float return statement with an Angle in `ArcSin(float)`
= Replaced the float return statement with an Angle in `ArcTan(float)`
= Replaced the float return statement with an Angle in `ArcTan2(float, float)`
= Optimized `ArcCos(float)` to use presets
= Optimized `Divide(float, params float[])` to use other functions.
= Optimized `Divide(int, params int[])` to use other functions.
= Optimized `Subtract(float, params float[])` to subtract a sum instead of individually subtracting
= Optimized `Subtract(int, params int[])` to subtract a sum instead of individually subtracting
= Made `Clamp(int, int, int)` not remake code and use `Clamp(float, float, float)`
= Made `Lerp(int, int, float, bool)` cast to an int instead of flooring
= Made `Lerp(float, float, float, bool)` not break when clamping and A is greater than B
= Made `Median<T>(params T[])` not use an unneccesary average
= Made `Power(int, int)` not care about absolutes
= Made `Variance(params float[])` correctly calculate variance.
* Quaternion
= Made `GetAngle()` not create an angle out of `ArcCos(float)`, as it already is one
= Made `ToAngles()` not create angles out of arc trig functions, as they are already angles
* Samples
* Constants
= Swapped `DegToRad` and `RadToDeg`
* Equations
= Moved `static Scale(Equation, float, ScaleType)` to `EquationExtension`
= Moved `ScaleType` to `EquationExtension`
```

View File

@ -0,0 +1,12 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
[Serializable]
public class MathException : Nerd_STFException
{
public MathException() : base() { }
public MathException(string message) : base(message) { }
public MathException(string message, Exception inner) : base(message, inner) { }
protected MathException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

View File

@ -0,0 +1,12 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
[Serializable]
public class UndefinedException : MathException
{
public UndefinedException() : this("The equation calculated resulted in an undefined number.") { }
public UndefinedException(string message) : base(message) { }
public UndefinedException(string message, Exception inner) : base(message, inner) { }
protected UndefinedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

View File

@ -0,0 +1,19 @@
namespace Nerd_STF.Extensions;
public static class EquationExtension
{
public static Equation Scale(this Equation equ, float value, ScaleType type = ScaleType.Both) => type switch
{
ScaleType.X => x => equ(value / x),
ScaleType.Y => x => x * equ(value),
ScaleType.Both => x => x * equ(value / x),
_ => throw new ArgumentException("Unknown scale type " + type)
};
public enum ScaleType
{
X = 1,
Y = 2,
Both = X | Y
}
}

View File

@ -112,14 +112,6 @@ public struct HSVA : IColor, IEquatable<HSVA>
public static HSVA Lerp(HSVA a, HSVA b, float t, bool clamp = true) =>
new(Angle.Lerp(a.H, b.H, t, clamp), Mathf.Lerp(a.S, b.S, t, clamp), Mathf.Lerp(a.V, b.V, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
public static HSVA LerpSquared(HSVA a, HSVA b, float t, Angle.Type type = Angle.Type.Normalized,
bool clamp = true)
{
HSVA val = Lerp(a * a, b * b, t, clamp);
float H = Mathf.Sqrt(val.H.ValueFromType(type)), S = Mathf.Sqrt(val.S), V = Mathf.Sqrt(val.V),
A = Mathf.Sqrt(val.A);
return new(new Angle(H, Angle.Type.Normalized), S, V, A);
}
public static HSVA Median(params HSVA[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
@ -235,9 +227,7 @@ public struct HSVA : IColor, IEquatable<HSVA>
public static HSVA operator +(HSVA a, HSVA b) => new(a.H + b.H, a.S + b.S, a.V + b.V, a.A + b.A);
public static HSVA operator -(HSVA c) => new(1 - c.H.Normalized, 1 - c.S, 1 - c.V, c.A != 1 ? 1 - c.A : 1);
public static HSVA operator -(HSVA a, HSVA b) => new(a.H - b.H, a.S - b.S, a.V - b.V, a.A - b.A);
public static HSVA operator *(HSVA a, HSVA b) => new(a.H * b.H, a.S * b.S, a.V * b.V, a.A * b.A);
public static HSVA operator *(HSVA a, float b) => new(a.H * b, a.S * b, a.V * b, a.A * b);
public static HSVA operator /(HSVA a, HSVA b) => new(a.H / b.H, a.S / b.S, a.V / b.V, a.A / b.A);
public static HSVA operator /(HSVA a, float b) => new(a.H / b, a.S / b, a.V / b, a.A / b);
public static bool operator ==(HSVA a, RGBA b) => a.Equals(b);
public static bool operator !=(HSVA a, RGBA b) => !a.Equals(b);

View File

@ -86,8 +86,6 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public static HSVAByte Lerp(HSVAByte a, HSVAByte b, byte t, bool clamp = true) =>
new(Mathf.Lerp(a.H, b.H, t, clamp), Mathf.Lerp(a.S, b.S, t, clamp), Mathf.Lerp(a.V, b.V, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
public static HSVAByte LerpSquared(HSVAByte a, HSVAByte b, byte t, Angle.Type type, bool clamp = true) =>
HSVA.LerpSquared(a.ToHSVA(), b.ToHSVA(), t, type, clamp).ToHSVAByte();
public static HSVAByte Median(params HSVAByte[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);

View File

@ -40,11 +40,6 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
}
public static Vector3d Cross(Vector2d a, Vector2d b, bool normalized = false) =>
Float2.Cross(a.ToXYZ(), b.ToXYZ(), normalized).ToVector();
public static Vector2d Divide(Vector2d num, params Vector2d[] vals)
{
foreach (Vector2d v in vals) num /= v;
return num;
}
public static float Dot(Vector2d a, Vector2d b) => Float2.Dot(a.ToXYZ(), b.ToXYZ());
public static float Dot(params Vector2d[] vals)
{
@ -76,13 +71,6 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
foreach (Vector2d f in vals) val = f < val ? f : val;
return val;
}
public static Vector2d Product(params Vector2d[] vals)
{
if (vals.Length < 1) return Zero;
Vector2d val = One;
foreach (Vector2d v in vals) val *= v;
return val;
}
public static Vector2d Round(Vector2d val, Angle.Type angleRound = Angle.Type.Degrees) =>
new(Angle.Round(val.theta, angleRound), Mathf.Round(val.magnitude));
public static Vector2d Subtract(Vector2d num, params Vector2d[] vals)
@ -127,18 +115,14 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
public object Clone() => new Vector2d(theta, magnitude);
public Float2 ToXYZ() => new(Mathf.Cos(theta));
public Float2 ToXYZ() => new Float2(Mathf.Cos(theta), Mathf.Sin(theta)) * magnitude;
public static Vector2d operator +(Vector2d a, Vector2d b) => new(a.theta + b.theta, a.magnitude + b.magnitude);
public static Vector2d operator -(Vector2d v) => v.Inverse;
public static Vector2d operator -(Vector2d a, Vector2d b) => new(a.theta - b.theta, a.magnitude - b.magnitude);
public static Vector2d operator *(Vector2d a, Angle b) => new(a.theta * b, a.magnitude);
public static Vector2d operator *(Vector2d a, float b) => new(a.theta, a.magnitude * b);
public static Vector2d operator *(Vector2d a, Vector2d b) => new(a.theta * b.theta, a.magnitude * b.magnitude);
public static Vector2d operator *(Vector2d a, Matrix b) => (Vector2d)((Matrix)a * b);
public static Vector2d operator /(Vector2d a, Angle b) => new(a.theta / b, a.magnitude);
public static Vector2d operator /(Vector2d a, float b) => new(a.theta, a.magnitude / b);
public static Vector2d operator /(Vector2d a, Vector2d b) => new(a.theta / b.theta, a.magnitude / b.magnitude);
public static Vector2d operator /(Vector2d a, Matrix b) => (Vector2d)((Matrix)a / b);
public static bool operator ==(Vector2d a, Vector2d b) => a.Equals(b);
public static bool operator !=(Vector2d a, Vector2d b) => !a.Equals(b);

View File

@ -76,11 +76,6 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
}
public static Vector3d Cross(Vector3d a, Vector3d b, bool normalized = false) =>
Float3.Cross(a.ToXYZ(), b.ToXYZ(), normalized).ToVector();
public static Vector3d Divide(Vector3d num, params Vector3d[] vals)
{
foreach (Vector3d v in vals) num /= v;
return num;
}
public static float Dot(Vector3d a, Vector3d b) => Float3.Dot(a.ToXYZ(), b.ToXYZ());
public static float Dot(params Vector3d[] vals)
{
@ -113,13 +108,6 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
foreach (Vector3d f in vals) val = f < val ? f : val;
return val;
}
public static Vector3d Product(params Vector3d[] vals)
{
if (vals.Length < 1) return Zero;
Vector3d val = One;
foreach (Vector3d v in vals) val *= v;
return val;
}
public static Vector3d Round(Vector3d val, Angle.Type angleRound = Angle.Type.Degrees) =>
new(Angle.Round(val.yaw, angleRound), Angle.Round(val.pitch, angleRound), Mathf.Round(val.magnitude));
public static Vector3d Subtract(Vector3d num, params Vector3d[] vals)
@ -172,15 +160,9 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
public static Vector3d operator -(Vector3d v) => v.Inverse;
public static Vector3d operator -(Vector3d a, Vector3d b) => new(a.yaw - b.yaw, a.pitch - b.pitch,
a.magnitude - b.magnitude);
public static Vector3d operator *(Vector3d a, Angle b) => new(a.yaw * b, a.pitch * b, a.magnitude);
public static Vector3d operator *(Vector3d a, float b) => new(a.yaw, a.pitch, a.magnitude * b);
public static Vector3d operator *(Vector3d a, Vector3d b) => new(a.yaw * b.yaw, a.pitch * b.pitch,
a.magnitude * b.magnitude);
public static Vector3d operator *(Vector3d a, Matrix b) => (Vector3d)((Matrix)a * b);
public static Vector3d operator /(Vector3d a, Angle b) => new(a.yaw / b, a.pitch / b, a.magnitude);
public static Vector3d operator /(Vector3d a, float b) => new(a.yaw, a.pitch, a.magnitude / b);
public static Vector3d operator /(Vector3d a, Vector3d b) => new(a.yaw / b.yaw, a.pitch / b.pitch,
a.magnitude / b.magnitude);
public static Vector3d operator /(Vector3d a, Matrix b) => (Vector3d)((Matrix)a / b);
public static bool operator ==(Vector3d a, Vector3d b) => a.Equals(b);
public static bool operator !=(Vector3d a, Vector3d b) => !a.Equals(b);

View File

@ -34,13 +34,12 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
set => p_deg = value * Constants.RadToDeg;
}
public Angle Bounded => new(p_deg % 360);
public Angle Bounded => new(Mathf.AbsoluteMod(p_deg, 360));
public Angle Reflected => new Angle(-p_deg).Bounded;
private float p_deg;
public Angle(float value, Type valueType = Type.Degrees)
{
p_deg = valueType switch
public Angle(float value, Type valueType = Type.Degrees) => p_deg = valueType switch
{
Type.Degrees => value,
Type.Gradians => value * 0.9f,
@ -48,7 +47,6 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
Type.Radians => value * Constants.RadToDeg,
_ => throw new ArgumentException("Unknown type.", nameof(valueType)),
};
}
public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg));
public static Angle Average(params Angle[] vals) => new(Mathf.Average(SplitArray(Type.Degrees, vals)));
@ -59,11 +57,27 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
new(Mathf.Floor(val.ValueFromType(type)), type);
public static Angle Lerp(Angle a, Angle b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp));
public static Angle Max(params Angle[] vals) => new(Mathf.Max(SplitArray(Type.Degrees, vals)));
public static Angle Max(params Angle[] vals) => Max(true, vals);
public static Angle Max(bool useBounded, params Angle[] vals)
{
if (!useBounded) return new(Mathf.Max(SplitArray(Type.Degrees, vals)));
Angle[] boundeds = new Angle[vals.Length];
for (int i = 0; i < vals.Length; i++) boundeds[i] = vals[i].Bounded;
return new(Mathf.Max(SplitArray(Type.Degrees, boundeds)));
}
public static Angle Median(params Angle[] vals) => new(Mathf.Median(SplitArray(Type.Degrees, vals)));
public static Angle Min(params Angle[] vals) => new(Mathf.Min(SplitArray(Type.Degrees, vals)));
public static Angle Min(params Angle[] vals) => Min(true, vals);
public static Angle Min(bool useBounded, params Angle[] vals)
{
if (!useBounded) return new(Mathf.Min(SplitArray(Type.Degrees, vals)));
Angle[] boundeds = new Angle[vals.Length];
for (int i = 0; i < vals.Length; i++) boundeds[i] = vals[i].Bounded;
return new(Mathf.Min(SplitArray(Type.Degrees, boundeds)));
}
public static Angle Round(Angle val, Type type = Type.Degrees) =>
new(Mathf.Floor(val.ValueFromType(type)), type);
new(Mathf.Round(val.ValueFromType(type)), type);
public static float[] SplitArray(Type outputType, params Angle[] vals)
{
@ -121,11 +135,9 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
};
public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg);
public static Angle operator -(Angle a) => new(-a.p_deg);
public static Angle operator -(Angle a) => new(a.p_deg + 180);
public static Angle operator -(Angle a, Angle b) => new(a.p_deg - b.p_deg);
public static Angle operator *(Angle a, Angle b) => new(a.p_deg * b.p_deg);
public static Angle operator *(Angle a, float b) => new(a.p_deg * b);
public static Angle operator /(Angle a, Angle b) => new(a.p_deg / b.p_deg);
public static Angle operator /(Angle a, float b) => new(a.p_deg / b);
public static bool operator ==(Angle a, Angle b) => a.Equals(b);
public static bool operator !=(Angle a, Angle b) => !a.Equals(b);

View File

@ -4,16 +4,8 @@ public static class Calculus
{
public const float DefaultStep = 0.001f;
public static Equation GetDerivative(Equation equ, float min, float max, float step = DefaultStep)
{
Dictionary<float, float> vals = new();
for (float x = min; x <= max; x += step)
{
float val1 = equ(x), val2 = equ(x + step), change = (val2 - val1) / step;
vals.Add(x, change);
}
return Mathf.MakeEquation(vals);
}
public static Equation GetDerivative(Equation equ, float step = DefaultStep) =>
x => GetDerivativeAtPoint(equ, x, step);
public static float GetDerivativeAtPoint(Equation equ, float x, float step = DefaultStep) =>
(equ(x + DefaultStep) - equ(x)) / step;
@ -24,6 +16,12 @@ public static class Calculus
return val;
}
public static Equation GetDynamicIntegral(Equation equ, Equation lowerBound, Equation upperBound,
float step = DefaultStep) => x => GetIntegral(equ, lowerBound(x), upperBound(x), step);
// Unfortunately, I cannot test this function, as I have literally no idea how it works and
// I can't find any tools online (and couldn't make my own) to compare my results.
// Something to know, though I didn't feel like it deserved its own [Obsolete] attribute.
public static float GradientDescent(Equation equ, float initial, float rate, float stepCount = 1000,
float step = DefaultStep)
{

View File

@ -70,11 +70,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
}
public static Float3 Cross(Float2 a, Float2 b, bool normalized = false) =>
Float3.Cross(a, b, normalized);
public static Float2 Divide(Float2 num, params Float2[] vals)
{
foreach (Float2 f in vals) num /= f;
return num;
}
public static Float2 Divide(Float2 num, params Float2[] vals) => num / Product(vals);
public static float Dot(Float2 a, Float2 b) => a.x * b.x + a.y * b.y;
public static float Dot(params Float2[] vals)
{
@ -93,9 +89,9 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Float2 Median(params Float2[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Float2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) * 0.5f;
}
public static Float2 Max(params Float2[] vals)
{
@ -120,11 +116,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
}
public static Float2 Round(Float2 val) =>
new(Mathf.Round(val.x), Mathf.Round(val.y));
public static Float2 Subtract(Float2 num, params Float2[] vals)
{
foreach (Float2 f in vals) num -= f;
return num;
}
public static Float2 Subtract(Float2 num, params Float2[] vals) => num - Sum(vals);
public static Float2 Sum(params Float2[] vals)
{
Float2 val = Zero;
@ -174,7 +166,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
}
public List<float> ToList() => new() { x, y };
public Vector2d ToVector() => new(new(Mathf.ArcTan(y / x), Angle.Type.Radians), Magnitude);
public Vector2d ToVector() => new(Mathf.ArcTan(y / x), Magnitude);
public static Float2 operator +(Float2 a, Float2 b) => new(a.x + b.x, a.y + b.y);
public static Float2 operator -(Float2 d) => new(-d.x, -d.y);

View File

@ -89,11 +89,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Float3 Divide(Float3 num, params Float3[] vals)
{
foreach (Float3 d in vals) num /= d;
return num;
}
public static Float3 Divide(Float3 num, params Float3[] vals) => num / Product(vals);
public static float Dot(Float3 a, Float3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static float Dot(params Float3[] vals)
{
@ -113,9 +109,9 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Float3 Median(params Float3[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Float3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) * 0.5f;
}
public static Float3 Max(params Float3[] vals)
{
@ -140,11 +136,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
}
public static Float3 Round(Float3 val) =>
new(Mathf.Round(val.x), Mathf.Round(val.y), Mathf.Round(val.z));
public static Float3 Subtract(Float3 num, params Float3[] vals)
{
foreach (Float3 d in vals) num -= d;
return num;
}
public static Float3 Subtract(Float3 num, params Float3[] vals) => num - Sum(vals);
public static Float3 Sum(params Float3[] vals)
{
Float3 val = Zero;
@ -199,7 +191,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public Vector3d ToVector()
{
float mag = Magnitude;
return new(new Angle(Mathf.ArcTan(y / x), Angle.Type.Radians), new(Mathf.ArcCos(z / mag), Angle.Type.Radians), mag);
return new(Mathf.ArcTan(y / x), Mathf.ArcCos(z / mag), mag);
}
public static Float3 operator +(Float3 a, Float3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);

View File

@ -4,9 +4,15 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
{
public static Float4 Back => new(0, 0, -1, 0);
public static Float4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Float4 Far => new(0, 0, 0, 1);
public static Float4 Forward => new(0, 0, 1, 0);
public static Float4 HighW => new(0, 0, 0, 1);
public static Float4 Left => new(-1, 0, 0, 0);
public static Float4 LowW => new(0, 0, 0, -1);
[Obsolete("Field has been replaced by " + nameof(LowW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Float4 Near => new(0, 0, 0, -1);
public static Float4 Right => new(1, 0, 0, 0);
public static Float4 Up => new(0, 1, 0, 0);
@ -100,11 +106,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Float4 Divide(Float4 num, params Float4[] vals)
{
foreach (Float4 d in vals) num /= d;
return num;
}
public static Float4 Divide(Float4 num, params Float4[] vals) => num / Product(vals);
public static float Dot(Float4 a, Float4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static float Dot(params Float4[] vals)
{
@ -126,9 +128,9 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
Mathf.Lerp(a.w, b.w, t, clamp));
public static Float4 Median(params Float4[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Float4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) * 0.5f;
}
public static Float4 Max(params Float4[] vals)
{
@ -153,11 +155,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
foreach (Float4 d in vals) val *= d;
return val;
}
public static Float4 Subtract(Float4 num, params Float4[] vals)
{
foreach (Float4 d in vals) num -= d;
return num;
}
public static Float4 Subtract(Float4 num, params Float4[] vals) => num - Sum(vals);
public static Float4 Sum(params Float4[] vals)
{
Float4 val = Zero;

View File

@ -67,11 +67,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
}
public static Int3 Cross(Int2 a, Int2 b, bool normalized = false) =>
Int3.Cross(a, b, normalized);
public static Int2 Divide(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num /= d;
return num;
}
public static Int2 Divide(Int2 num, params Int2[] vals) => num / Product(vals);
public static int Dot(Int2 a, Int2 b) => a.x * b.x + a.y * b.y;
public static int Dot(params Int2[] vals)
{
@ -88,9 +84,9 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Int2 Median(params Int2[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Int2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) / 2;
}
public static Int2 Max(params Int2[] vals)
{
@ -113,11 +109,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
foreach (Int2 d in vals) val *= d;
return val;
}
public static Int2 Subtract(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num -= d;
return num;
}
public static Int2 Subtract(Int2 num, params Int2[] vals) => num - Sum(vals);
public static Int2 Sum(params Int2[] vals)
{
Int2 val = Zero;

View File

@ -86,11 +86,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Int3 Divide(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num /= d;
return num;
}
public static Int3 Divide(Int3 num, params Int3[] vals) => num / Product(vals);
public static int Dot(Int3 a, Int3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static int Dot(params Int3[] vals)
{
@ -108,9 +104,9 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Int3 Median(params Int3[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Int3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) / 2;
}
public static Int3 Max(params Int3[] vals)
{
@ -133,11 +129,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
foreach (Int3 d in vals) val *= d;
return val;
}
public static Int3 Subtract(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num -= d;
return num;
}
public static Int3 Subtract(Int3 num, params Int3[] vals) => num - Sum(vals);
public static Int3 Sum(params Int3[] vals)
{
Int3 val = Zero;

View File

@ -5,9 +5,13 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
public static Int4 Back => new(0, 0, -1, 0);
public static Int4 Deep => new(0, 0, 0, -1);
public static Int4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Int4 Far => new(0, 0, 0, 1);
public static Int4 Forward => new(0, 0, 1, 0);
public static Int4 HighW => new(0, 0, 0, 1);
public static Int4 Left => new(-1, 0, 0, 0);
public static Int4 LowW => new(0, 0, 0, -1);
public static Int4 Right => new(1, 0, 0, 0);
public static Int4 Up => new(0, 1, 0, 0);
@ -97,11 +101,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int4 Divide(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num /= d;
return num;
}
public static Int4 Divide(Int4 num, params Int4[] vals) => num / Product(vals);
public static int Dot(Int4 a, Int4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static int Dot(params Int4[] vals)
{
@ -121,9 +121,9 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
Mathf.Lerp(a.w, b.w, t, clamp));
public static Int4 Median(params Int4[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
float index = (vals.Length - 1) * 0.5f;
Int4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
return (valA + valB) / 2;
}
public static Int4 Max(params Int4[] vals)
{
@ -146,11 +146,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
foreach (Int4 d in vals) val *= d;
return val;
}
public static Int4 Subtract(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num -= d;
return num;
}
public static Int4 Subtract(Int4 num, params Int4[] vals) => num - Sum(vals);
public static Int4 Sum(params Int4[] vals)
{
Int4 val = Zero;

View File

@ -5,19 +5,32 @@ public static class Mathf
public static float Absolute(float val) => val < 0 ? -val : val;
public static int Absolute(int val) => val < 0 ? -val : val;
public static float ArcCos(float value) => -ArcSin(value) + Constants.HalfPi;
public static float AbsoluteMod(float val, float mod)
{
while (val >= mod) val -= mod;
while (val < 0) val += mod;
return val;
}
public static int AbsoluteMod(int val, int mod)
{
while (val >= mod) val -= mod;
while (val < 0) val += mod;
return val;
}
public static float ArcCot(float value) => ArcCos(value / Sqrt(1 + value * value));
public static Angle ArcCos(float value) => ArcSin(-value) + Angle.Quarter;
public static float ArcCsc(float value) => ArcSin(1 / value);
public static Angle ArcCot(float value) => ArcCos(value / Sqrt(1 + value * value));
public static float ArcSec(float value) => ArcCos(1 / value);
public static Angle ArcCsc(float value) => ArcSin(1 / value);
public static Angle ArcSec(float value) => ArcCos(1 / value);
// Maybe one day I'll have a polynomial for this, but the RMSE for an order 10 polynomial is only 0.00876.
public static float ArcSin(float value) => (float)Math.Asin(value);
public static Angle ArcSin(float value) => new((float)Math.Asin(value), Angle.Type.Radians);
public static float ArcTan(float value) => ArcSin(value / Sqrt(1 + value * value));
public static float ArcTan2(float a, float b) => ArcTan(a / b);
public static Angle ArcTan(float value) => ArcSin(value / Sqrt(1 + value * value));
public static Angle ArcTan2(float a, float b) => ArcTan(a / b);
public static float Average(Equation equ, float min, float max, float step = Calculus.DefaultStep)
{
@ -26,9 +39,16 @@ public static class Mathf
return Average(vals.ToArray());
}
public static float Average(params float[] vals) => Sum(vals) / vals.Length;
public static float Average(params int[] vals) => Sum(vals) / (float)vals.Length;
public static int Average(params int[] vals) => Sum(vals) / vals.Length;
public static int Ceiling(float val) => (int)(val + (1 - (val % 1)));
public static float Binomial(int n, int total, float successRate) =>
Combinations(total, n) * Power(successRate, n) * Power(1 - successRate, total - n);
public static int Ceiling(float val)
{
float mod = val % 1;
return (int)(mod == 0 ? val : (val + (1 - mod)));
}
public static float Clamp(float val, float min, float max)
{
@ -38,14 +58,7 @@ public static class Mathf
val = val > max ? max : val;
return val;
}
public static int Clamp(int val, int min, int max)
{
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
nameof(max) + " must be greater than or equal to " + nameof(min));
val = val < min ? min : val;
val = val > max ? max : val;
return val;
}
public static int Clamp(int val, int min, int max) => (int)Clamp((float)val, min, max);
// nCr (n = total, r = size)
public static int Combinations(int total, int size) => Factorial(total) /
@ -60,16 +73,8 @@ public static class Mathf
public static float Csc(Angle angle) => Csc(angle.Radians);
public static float Csc(float radians) => 1 / Sin(radians);
public static float Divide(float val, params float[] dividends)
{
foreach (float d in dividends) val /= d;
return val;
}
public static int Divide(int val, params int[] dividends)
{
foreach (int i in dividends) val /= i;
return val;
}
public static float Divide(float val, params float[] dividends) => val / Product(dividends);
public static int Divide(int val, params int[] dividends) => val / Product(dividends);
public static float Dot(float[] a, float[] b)
{
@ -98,6 +103,15 @@ public static class Mathf
return val;
}
public static int[] Factors(int val)
{
List<int> factors = new();
factors.Add(1);
for (int i = 2; i < val; i++) if (val % i == 0) factors.Add(i);
factors.Add(val);
return factors.ToArray();
}
public static int Floor(float val) => (int)(val - (val % 1));
public static Dictionary<float, float> GetValues(Equation equ, float min, float max,
@ -127,23 +141,51 @@ public static class Mathf
public static float Lerp(float a, float b, float t, bool clamp = true)
{
float v = a + t * (b - a);
if (clamp) v = Clamp(v, a, b);
if (clamp) v = Clamp(v, Min(a, b), Max(a, b));
return v;
}
public static int Lerp(int a, int b, float value, bool clamp = true) => Floor(Lerp(a, b, value, clamp));
public static int Lerp(int a, int b, float value, bool clamp = true) => (int)Lerp((float)a, b, value, clamp);
public static Equation MakeEquation(Dictionary<float, float> vals) => (x) =>
public static Equation MakeEquation(Dictionary<float, float> vals) => delegate (float x)
{
float min = -1, max = -1;
foreach (KeyValuePair<float, float> val in vals)
{
if (val.Key <= x) min = val.Key;
if (val.Key >= x) max = val.Key;
if (vals.Count < 1) throw new UndefinedException();
if (vals.Count == 1) return vals.Values.First();
if (min != -1 && max != -1) break;
if (vals.ContainsKey(x)) return vals[x];
float? min, max;
if (x < (min = vals.Keys.Min()))
{
max = vals.Keys.Where(x => x != min).Min();
float distX = x - min.Value, distAB = max.Value - min.Value;
return Lerp(vals[min.Value], vals[max.Value], distX / distAB, false);
}
float per = x % (max - min);
return Lerp(min, max, per);
else if (x > (max = vals.Keys.Max()))
{
min = vals.Keys.Where(x => x != max).Max();
float distX = x - min.Value, distAB = max.Value - min.Value;
return Lerp(vals[min.Value], vals[max.Value], distX / distAB, false);
}
float curDistMax = float.MaxValue, curDistMin = float.MaxValue;
foreach (float keyX in vals.Keys)
{
float dist = Absolute(keyX - x);
if (keyX < x && dist <= curDistMin)
{
min = keyX;
curDistMin = dist;
}
if (keyX > x && dist <= curDistMax)
{
max = keyX;
curDistMax = dist;
}
}
if (!min.HasValue || !max.HasValue || min == max) throw new UndefinedException();
float all = max.Value - min.Value, diff = x - min.Value;
return Lerp(vals[min.Value], vals[max.Value], diff / all);
};
public static float Max(Equation equ, float min, float max, float step = Calculus.DefaultStep)
@ -180,12 +222,13 @@ public static class Mathf
public static float Median(params float[] vals)
{
float index = Average(0, vals.Length - 1);
float valA = vals[Floor(index)], valB = vals[Ceiling(index)];
return Average(valA, valB);
float index = (vals.Length - 1) * 0.5f;
if (index % 1 == 0) return vals[(int)index];
float valA = vals[(int)index], valB = vals[(int)index + 1];
return (valA + valB) * 0.5f;
}
public static int Median(params int[] vals) => Median<int>(vals);
public static T Median<T>(params T[] vals) => vals[Floor(Average(0, vals.Length - 1))];
public static T Median<T>(params T[] vals) => vals[(vals.Length - 1) / 2];
public static float Min(Equation equ, float min, float max, float step = Calculus.DefaultStep)
{
@ -262,22 +305,34 @@ public static class Mathf
public static float Power(float num, float pow) => (float)Math.Pow(num, pow);
public static float Power(float num, int pow)
{
if (pow < 0) return 0;
if (pow <= 0) return 0;
if (pow == 1) return num;
float val = 1;
for (int i = 0; i < Absolute(pow); i++) val *= num;
float abs = Absolute(pow);
for (int i = 0; i < abs; i++) val *= num;
if (pow < 1) val = 1 / val;
return val;
}
public static int Power(int num, int pow)
{
if (pow < 0) return 0;
if (pow == 1) return num;
if (pow < 1) return 0;
int val = 1;
for (int i = 0; i < Absolute(pow); i++) val *= num;
if (pow < 1) val = 1 / val;
for (int i = 0; i < pow; i++) val *= num;
return val;
}
public static float Root(float value, float index) => (float)Math.Exp(index * Math.Log(value));
public static int PowerMod(int num, int pow, int mod)
{
if (pow == 1) return num;
if (pow < 1) return 0;
int val = 1;
int abs = Absolute(pow);
for (int i = 0; i < abs; i++) val = val * num % mod;
return val;
}
public static float Root(float value, float index) => (float)Math.Exp(Math.Log(value) / index);
public static float Round(float num) => num % 1 >= 0.5 ? Ceiling(num) : Floor(num);
public static float Round(float num, float nearest) => nearest * Round(num / nearest);
@ -300,7 +355,7 @@ public static class Mathf
h = -0.000577413f,
i = 0.0000613134f,
j = -0.00000216852f;
float x = radians % Constants.Tau;
float x = AbsoluteMod(radians, Constants.Tau);
return
a + (b * x) + (c * x * x) + (d * x * x * x) + (e * x * x * x * x) + (f * x * x * x * x * x)
@ -310,16 +365,8 @@ public static class Mathf
public static float Sqrt(float value) => Root(value, 2);
public static float Subtract(float num, params float[] vals)
{
foreach (float d in vals) num -= d;
return num;
}
public static int Subtract(int num, params int[] vals)
{
foreach (int i in vals) num -= i;
return num;
}
public static float Subtract(float num, params float[] vals) => num - Sum(vals);
public static int Subtract(int num, params int[] vals) => num - Sum(vals);
public static float Sum(params float[] vals)
{
@ -361,7 +408,7 @@ public static class Mathf
float val = vals[i] - mean;
sum += val * val;
}
return sum / vals.Length;
return sum / (vals.Length - 1);
}
public static float ZScore(float val, params float[] vals) => ZScore(val, Average(vals), StandardDeviation(vals));

View File

@ -11,6 +11,7 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public static Complex Zero => new(0, 0);
public Complex Conjugate => new(u, -i);
public Complex Inverse => Conjugate / (u * u + i * i);
public float Magnitude => Mathf.Sqrt(u * u + i * i);
public Complex Normalized => this * Mathf.InverseSqrt(u * u + i * i);
@ -125,6 +126,8 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
return (Us, Is);
}
public Angle GetAngle() => Mathf.ArcTan(i / u);
public int CompareTo(Complex other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
@ -135,9 +138,9 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public override int GetHashCode() => u.GetHashCode() ^ i.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
u.ToString(provider) + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i";
u.ToString(provider) + (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i";
public string ToString(IFormatProvider provider) =>
u.ToString(provider) + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i";
u.ToString(provider) + (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i";
public object Clone() => new Complex(u, i);
@ -164,11 +167,7 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public static Complex operator *(Complex a, Complex b) => new(a.u * b.u - a.i * b.i, a.u * b.i + a.i * b.u);
public static Complex operator *(Complex a, float b) => new(a.u * b, a.i * b);
public static Complex operator *(Complex a, Matrix b) => (Complex)((Matrix)a * b);
public static Complex operator /(Complex a, Complex b)
{
float c = b.u * b.u + b.i * b.i;
return new((a.u * b.u + a.i * b.i) / c, (a.i * b.u - a.u * b.i) / c);
}
public static Complex operator /(Complex a, Complex b) => a * b.Inverse;
public static Complex operator /(Complex a, float b) => new(a.u / b, a.i / b);
public static Complex operator /(Complex a, Matrix b) => (Complex)((Matrix)a / b);
public static bool operator ==(Complex a, Complex b) => a.Equals(b);

View File

@ -15,6 +15,7 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
public static Quaternion Zero => new(0, 0, 0, 0);
public Quaternion Conjugate => new(u, -i, -j, -k);
public Quaternion Inverse => Conjugate / (u * u + i * i + j * j + k * k);
public float Magnitude => Mathf.Sqrt(u * u + i * i + j * j + k * k);
public Quaternion Normalized => this * Mathf.InverseSqrt(u * u + i * i + j * j + k * k);
@ -135,6 +136,7 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
return Float4.Sum(floats.ToArray());
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public static Quaternion FromAngles(Angle yaw, Angle pitch, Angle? roll = null)
{
roll ??= Angle.Zero;
@ -151,8 +153,10 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
cosYawSinPitch * cosRoll + sinYawCosPitch * sinRoll,
sinYawCosPitch * cosRoll + cosYawSinPitch * sinRoll);
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public static Quaternion FromAngles(Float3 vals, Angle.Type valType) =>
FromAngles(new(vals.x, valType), new(vals.y, valType), new(vals.z, valType));
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public static Quaternion FromVector(Vector3d vec) => FromAngles(vec.yaw, vec.pitch);
public static (float[] Us, float[] Is, float[] Js, float[] Ks) SplitArray(params Quaternion[] vals)
@ -179,17 +183,19 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
public override int GetHashCode() => u.GetHashCode() ^ i.GetHashCode() ^ j.GetHashCode() ^ k.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => u.ToString(provider)
+ (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + j.ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + k.ToString(provider) + "k";
+ (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + Mathf.Absolute(j).ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + Mathf.Absolute(k).ToString(provider) + "k";
public string ToString(IFormatProvider provider) => u.ToString(provider)
+ (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + j.ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + k.ToString(provider) + "k";
+ (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + Mathf.Absolute(j).ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + Mathf.Absolute(k).ToString(provider) + "k";
public object Clone() => new Quaternion(u, i, j, k);
public Angle GetAngle() => new(2 * Mathf.ArcCos(u), Angle.Type.Radians);
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Angle GetAngle() => Mathf.ArcCos(u) * 2;
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Float3 GetAxis()
{
Float3 axis = IJK;
@ -198,6 +204,7 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
if (mag < 0) return Float3.Zero;
return axis / mag;
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public (Angle yaw, Angle pitch, Angle roll) ToAngles()
{
Quaternion doubled = this;
@ -221,19 +228,20 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
rotMatrix.r1c2 = 2 * (i * j - u * k);
rotMatrix.r1c3 = 2 * (i * k + u * j);
yaw = new(Mathf.ArcTan2(-rotMatrix.r1c2, -rotMatrix.r3c1 * rotMatrix.r1c3), Angle.Type.Radians);
yaw = Mathf.ArcTan2(-rotMatrix.r1c2, -rotMatrix.r3c1 * rotMatrix.r1c3);
pitch = new(-Constants.HalfPi * rotMatrix.r3c1 / r3c1Abs, Angle.Type.Radians);
roll = Angle.Zero;
}
else
{
yaw = new(Mathf.ArcTan2(rotMatrix.r2c1, rotMatrix.r1c1), Angle.Type.Radians);
pitch = new(Mathf.ArcSin(-rotMatrix.r3c1), Angle.Type.Radians);
roll = new(Mathf.ArcTan2(rotMatrix.r3c2, rotMatrix.r3c3), Angle.Type.Radians);
yaw = Mathf.ArcTan2(rotMatrix.r2c1, rotMatrix.r1c1);
pitch = Mathf.ArcSin(-rotMatrix.r3c1);
roll = Mathf.ArcTan2(rotMatrix.r3c2, rotMatrix.r3c3);
}
return (yaw, pitch, roll);
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Vector3d ToVector()
{
(Angle yaw, Angle pitch, _) = ToAngles();
@ -274,16 +282,7 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
public static Quaternion operator *(Quaternion a, float b) => new(a.u * b, a.i * b, a.j * b, a.k * b);
public static Quaternion operator *(Quaternion a, Matrix b) => (Quaternion)((Matrix)a * b);
public static Quaternion operator *(Quaternion a, Float3 b) => a * new Quaternion(b);
public static Quaternion operator /(Quaternion x, Quaternion y)
{
float a = x.u, b = x.i, c = x.j, d = x.k, e = y.u, f = y.i, g = y.j, h = y.k,
u = a * e + b * f + c * g + d * h,
i = b * e + c * h + d * g - a * f,
j = c * e + d * f - a * g - b * h,
k = c * f + d * e - a * h - b * g,
q = e * e + f * f + g * g + h * h;
return new(u / q, i / q, j / q, k / q);
}
public static Quaternion operator /(Quaternion x, Quaternion y) => x * y.Inverse;
public static Quaternion operator /(Quaternion a, float b) => new(a.u / b, a.i / b, a.j / b, a.k / b);
public static Quaternion operator /(Quaternion a, Matrix b) => (Quaternion)((Matrix)a / b);
public static Quaternion operator /(Quaternion a, Float3 b) => a / new Quaternion(b);

View File

@ -2,10 +2,10 @@
public static class Constants
{
public const float DegToRad = 180 / Pi;
public const float DegToRad = Pi / 180;
public const float HalfPi = Pi / 2;
public const float Pi = 3.14159265359f;
public const float RadToDeg = Pi / 180;
public const float RadToDeg = 180 / Pi;
public const float Tau = Pi * 2;
public const float E = 2.71828182846f;

View File

@ -8,19 +8,4 @@ public static class Equations
public static readonly Equation SinWave = x => Mathf.Sin(x);
public static readonly Equation SawWave = x => x % 1;
public static readonly Equation SquareWave = x => x % 2 < 1 ? 1 : 0;
public static Equation Scale(Equation equ, float value, ScaleType type = ScaleType.Both) => type switch
{
ScaleType.X => x => equ(value / x),
ScaleType.Y => x => x * equ(value),
ScaleType.Both => x => x * equ(value / x),
_ => throw new ArgumentException("Unknown scale type " + type)
};
public enum ScaleType
{
X = 1,
Y = 2,
Both = X | Y
}
}