Prerequisites for matrices and 2x2 matrix done.
This commit is contained in:
parent
fcee608322
commit
36d4411d70
@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
{
|
||||
public interface ICombinationIndexer<TItem>
|
||||
{
|
||||
IEnumerable<TItem> this[string key] { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,7 @@
|
||||
using System.Text;
|
||||
using Nerd_STF.Mathematics;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerd_STF.Helpers
|
||||
{
|
||||
@ -48,5 +51,64 @@ namespace Nerd_STF.Helpers
|
||||
}
|
||||
return builder.Remove(builder.Length - 1, 1).ToString();
|
||||
}
|
||||
|
||||
#if CS8_OR_GREATER
|
||||
public static string MatrixToString<T>(T matrix, string? format)
|
||||
#else
|
||||
public static string MatrixToString<T>(T matrix, string format)
|
||||
#endif
|
||||
where T : IMatrix<T>
|
||||
{
|
||||
// First convert all items to their string counterparts,
|
||||
// then measure the lengths and do spacing accordingly.
|
||||
Int2 size = matrix.Size;
|
||||
string[,] items = new string[size.x, size.y];
|
||||
for (int x = 0; x < size.x; x++) for (int y = 0; y < size.y; y++)
|
||||
items[x, y] = matrix[y, x].ToString(format);
|
||||
|
||||
// Then write each line separately.
|
||||
StringBuilder[] lines = new StringBuilder[size.y + 2];
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (i == 0) builder.Append('┌');
|
||||
else if (i == lines.Length - 1) builder.Append('└');
|
||||
else builder.Append('│');
|
||||
|
||||
lines[i] = builder;
|
||||
}
|
||||
int totalLen = 0;
|
||||
for (int x = 0; x < size.x; x++)
|
||||
{
|
||||
int maxLen = 0;
|
||||
for (int y = 0; y < size.y; y++)
|
||||
{
|
||||
string item = items[x, y];
|
||||
if (item.Length > maxLen) maxLen = item.Length;
|
||||
}
|
||||
totalLen += maxLen + 1;
|
||||
for (int y = 0; y < size.y; y++)
|
||||
{
|
||||
StringBuilder builder = lines[y + 1];
|
||||
string item = items[x, y];
|
||||
int spacing = maxLen - item.Length;
|
||||
builder.Append(new string(' ', spacing + 1));
|
||||
builder.Append(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish up and merge.
|
||||
StringBuilder total = new StringBuilder();
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
StringBuilder builder = lines[i];
|
||||
if (i == 0) builder.Append(new string(' ', totalLen)).Append(" ┐");
|
||||
else if (i == lines.Length - 1) builder.Append(new string(' ', totalLen)).Append(" ┘");
|
||||
else builder.Append(" │");
|
||||
total.Append(builder);
|
||||
if (i != lines.Length - 1) total.AppendLine();
|
||||
}
|
||||
return total.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
Nerd_STF/ICombinationIndexer.cs
Normal file
7
Nerd_STF/ICombinationIndexer.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Nerd_STF
|
||||
{
|
||||
public interface ICombinationIndexer<TItem>
|
||||
{
|
||||
ListTuple<TItem> this[string key] { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#if CS11_OR_GREATER
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF
|
||||
{
|
||||
public interface IFromTuple<TSelf, TTuple>
|
||||
where TSelf : IFromTuple<TSelf, TTuple>
|
||||
@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF
|
||||
{
|
||||
public interface ISplittable<TSelf, TTuple>
|
||||
where TSelf : ISplittable<TSelf, TTuple>
|
||||
134
Nerd_STF/ListTuple.cs
Normal file
134
Nerd_STF/ListTuple.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerd_STF
|
||||
{
|
||||
public readonly struct ListTuple<T> : IEnumerable<T>,
|
||||
IEquatable<ListTuple<T>>
|
||||
#if NET471_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
||||
,ITuple
|
||||
#endif
|
||||
{
|
||||
public int Length => items.Length;
|
||||
|
||||
private readonly T[] items;
|
||||
|
||||
public ListTuple(IEnumerable<T> items)
|
||||
{
|
||||
this.items = items.ToArray();
|
||||
}
|
||||
public ListTuple(params T[] items)
|
||||
{
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => items[index];
|
||||
set => items[index] = value;
|
||||
}
|
||||
#if NET471_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
||||
#if CS8_OR_GREATER
|
||||
object? ITuple.this[int index] => this[index];
|
||||
#else
|
||||
object ITuple.this[int index] => this[index];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public bool Equals(ListTuple<T> other)
|
||||
{
|
||||
if (Length != other.Length) return false;
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
T itemA = items[i], itemB = other.items[i];
|
||||
if (itemA == null || itemB == null)
|
||||
{
|
||||
if (itemA == null && itemB == null) continue;
|
||||
else return false;
|
||||
}
|
||||
if (!itemA.Equals(itemB)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#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 ListTuple<T> otherTuple) return Equals(otherTuple);
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => items.GetHashCode();
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("(");
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
builder.Append(items[i]);
|
||||
if (i != items.Length - 1) builder.Append(", ");
|
||||
}
|
||||
builder.Append(')');
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(ListTuple<T> a, ListTuple<T> b) => a.Equals(b);
|
||||
public static bool operator !=(ListTuple<T> a, ListTuple<T> b) => !a.Equals(b);
|
||||
|
||||
public static implicit operator ValueTuple<T>(ListTuple<T> tuple) => new ValueTuple<T>(tuple[0]);
|
||||
public static implicit operator ValueTuple<T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1]);
|
||||
public static implicit operator ValueTuple<T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2]);
|
||||
public static implicit operator ValueTuple<T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
public static implicit operator ValueTuple<T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4]);
|
||||
public static implicit operator ValueTuple<T, T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5]);
|
||||
public static implicit operator ValueTuple<T, T, T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5], tuple[6]);
|
||||
public static implicit operator T[](ListTuple<T> tuple) => tuple.items;
|
||||
|
||||
public static implicit operator ListTuple<T>(ValueTuple<T> tuple) => new ListTuple<T>(tuple.Item1);
|
||||
public static implicit operator ListTuple<T>((T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2);
|
||||
public static implicit operator ListTuple<T>((T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6, tuple.Item7);
|
||||
public static implicit operator ListTuple<T>(T[] array) => new ListTuple<T>(array);
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private int index;
|
||||
private readonly ListTuple<T> tuple;
|
||||
|
||||
public T Current => tuple.items[index];
|
||||
#if CS8_OR_GREATER
|
||||
object? IEnumerator.Current => Current;
|
||||
#else
|
||||
object IEnumerator.Current => Current;
|
||||
#endif
|
||||
public bool MoveNext()
|
||||
{
|
||||
index++;
|
||||
return index < tuple.items.Length;
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
public void Dispose() { }
|
||||
|
||||
internal Enumerator(ListTuple<T> tuple)
|
||||
{
|
||||
index = -1;
|
||||
this.tuple = tuple;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Nerd_STF/Mathematics/Algebra/IMatrix.cs
Normal file
36
Nerd_STF/Mathematics/Algebra/IMatrix.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface IMatrix<TSelf> : IEnumerable<double>,
|
||||
IEquatable<TSelf>,
|
||||
IFormattable
|
||||
#if CS11_OR_GREATER
|
||||
,IMatrixOperations<TSelf>
|
||||
#endif
|
||||
where TSelf : IMatrix<TSelf>
|
||||
{
|
||||
Int2 Size { get; }
|
||||
|
||||
double this[int r, int c] { get; set; }
|
||||
double this[Int2 index] { get; set; }
|
||||
ListTuple<double> this[int index, RowColumn direction] { get; set; }
|
||||
|
||||
ListTuple<double> GetRow(int row);
|
||||
ListTuple<double> GetColumn(int column);
|
||||
void SetRow(int row, IEnumerable<double> vals);
|
||||
void SetColumn(int column, IEnumerable<double> vals);
|
||||
|
||||
double Determinant();
|
||||
|
||||
TSelf Adjoint();
|
||||
TSelf Cofactor();
|
||||
TSelf Transpose();
|
||||
#if CS9_OR_GREATER
|
||||
TSelf? Inverse();
|
||||
#else
|
||||
TSelf Inverse();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
17
Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs
Normal file
17
Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs
Normal file
@ -0,0 +1,17 @@
|
||||
#if CS11_OR_GREATER
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface IMatrixOperations<TSelf> : ISimpleMathOperations<TSelf>
|
||||
where TSelf : IMatrix<TSelf>, IMatrixOperations<TSelf>
|
||||
{
|
||||
static abstract TSelf Average(IEnumerable<TSelf> vals);
|
||||
static abstract TSelf Lerp(TSelf a, TSelf b, double t, bool clamp = true);
|
||||
|
||||
static abstract TSelf operator *(TSelf a, double b);
|
||||
static abstract TSelf operator /(TSelf a, double b);
|
||||
static abstract TSelf? operator ~(TSelf m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
8
Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs
Normal file
8
Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface ISquareMatrix<TSelf> : IMatrix<TSelf>
|
||||
where TSelf : ISquareMatrix<TSelf>
|
||||
{
|
||||
double Trace();
|
||||
}
|
||||
}
|
||||
11
Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs
Normal file
11
Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs
Normal file
@ -0,0 +1,11 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface IStaticMatrix<TSelf> : IPresets1d<TSelf>, IMatrix<TSelf>
|
||||
where TSelf : IStaticMatrix<TSelf>
|
||||
{
|
||||
static abstract TSelf Identity { get; }
|
||||
static abstract TSelf SignField { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
9
Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs
Normal file
9
Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface ISubmatrixOperations<TSelf, TSmaller>
|
||||
where TSelf : IMatrix<TSelf>, ISubmatrixOperations<TSelf, TSmaller>
|
||||
where TSmaller : IMatrix<TSmaller>
|
||||
{
|
||||
TSmaller Submatrix(int r, int c);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#if CS11_OR_GREATER
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public interface IVectorOperations<TSelf> : ISimpleMathOperations<TSelf>
|
||||
where TSelf : IVectorOperations<TSelf>
|
||||
351
Nerd_STF/Mathematics/Algebra/Matrix2x2.cs
Normal file
351
Nerd_STF/Mathematics/Algebra/Matrix2x2.cs
Normal file
@ -0,0 +1,351 @@
|
||||
using Nerd_STF.Helpers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public class Matrix2x2 : IMatrix<Matrix2x2>,
|
||||
ISquareMatrix<Matrix2x2>
|
||||
#if CS11_OR_GREATER
|
||||
,ISplittable<Matrix2x2, (double[] r1c1, double[] r1c2, double[] r2c1, double[] r2c2)>,
|
||||
IStaticMatrix<Matrix2x2>
|
||||
#endif
|
||||
{
|
||||
public static Matrix2x2 Identity => new Matrix2x2(1, 0, 0, 1);
|
||||
public static Matrix2x2 SignField => new Matrix2x2(1, -1, -1, 1);
|
||||
|
||||
public static Matrix2x2 One => new Matrix2x2(1, 1, 1, 1);
|
||||
public static Matrix2x2 Zero => new Matrix2x2(0, 0, 0, 0);
|
||||
|
||||
public Int2 Size => (2, 2);
|
||||
|
||||
public double r1c1, r1c2,
|
||||
r2c1, r2c2;
|
||||
|
||||
public Matrix2x2()
|
||||
{
|
||||
r1c1 = 0;
|
||||
r1c2 = 0;
|
||||
r2c1 = 0;
|
||||
r2c2 = 0;
|
||||
}
|
||||
public Matrix2x2(Matrix2x2 copy)
|
||||
{
|
||||
r1c1 = copy.r1c1;
|
||||
r1c2 = copy.r1c2;
|
||||
r2c1 = copy.r2c1;
|
||||
r2c2 = copy.r2c2;
|
||||
}
|
||||
public Matrix2x2(double r1c1, double r1c2, double r2c1, double r2c2)
|
||||
{
|
||||
this.r1c1 = r1c1;
|
||||
this.r1c2 = r1c2;
|
||||
this.r2c1 = r2c1;
|
||||
this.r2c2 = r2c2;
|
||||
}
|
||||
/// <param name="byRows"><see langword="true"/> if the array is of the form [c, r], <see langword="false"/> if the array is of the form [r, c].</param>
|
||||
public Matrix2x2(double[,] vals, bool byRows = true)
|
||||
{
|
||||
if (byRows) // Collection of rows ([c, r])
|
||||
{
|
||||
r1c1 = vals[0, 0]; r1c2 = vals[0, 1];
|
||||
r2c1 = vals[1, 0]; r2c2 = vals[1, 1];
|
||||
}
|
||||
else // Collection of columns ([r, c])
|
||||
{
|
||||
r1c1 = vals[0, 0]; r1c2 = vals[1, 0];
|
||||
r2c1 = vals[0, 1]; r2c2 = vals[1, 1];
|
||||
}
|
||||
}
|
||||
/// <param name="byRows"><see langword="true"/> if the enumerable is a collection of rows (form [c, r]), <see langword="false"/> if the enumerable is a collection of columns (form [r, c]).</param>
|
||||
public Matrix2x2(IEnumerable<IEnumerable<double>> vals, bool byRows = true)
|
||||
{
|
||||
int x = 0;
|
||||
foreach (IEnumerable<double> part in vals)
|
||||
{
|
||||
int y = 0;
|
||||
foreach (double v in part)
|
||||
{
|
||||
this[byRows ? (x, y) : (y, x)] = v;
|
||||
y++;
|
||||
if (y >= 2) break;
|
||||
}
|
||||
x++;
|
||||
if (x >= 2) break;
|
||||
}
|
||||
}
|
||||
/// <param name="byRows"><see langword="true"/> if the enumerable is a collection of rows (form [c, r]), <see langword="false"/> if the enumerable is a collection of columns (form [r, c]).</param>
|
||||
public Matrix2x2(IEnumerable<ListTuple<double>> vals, bool byRows = true)
|
||||
{
|
||||
int x = 0;
|
||||
foreach (IEnumerable<double> part in vals)
|
||||
{
|
||||
int y = 0;
|
||||
foreach (double v in part)
|
||||
{
|
||||
this[byRows ? (x, y) : (y, x)] = v;
|
||||
y++;
|
||||
if (y >= 2) break;
|
||||
}
|
||||
x++;
|
||||
if (x >= 2) break;
|
||||
}
|
||||
}
|
||||
|
||||
public double this[int r, int c]
|
||||
{
|
||||
get => this[(r, c)];
|
||||
set => this[(r, c)] = value;
|
||||
}
|
||||
public double this[Int2 index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index.x) // (r, c)
|
||||
{
|
||||
case 0:
|
||||
switch (index.y)
|
||||
{
|
||||
case 0: return r1c1;
|
||||
case 1: return r1c2;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
case 1:
|
||||
switch (index.y)
|
||||
{
|
||||
case 0: return r2c1;
|
||||
case 1: return r2c2;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index.x) // (r, c)
|
||||
{
|
||||
case 0:
|
||||
switch (index.y)
|
||||
{
|
||||
case 0: r1c1 = value; return;
|
||||
case 1: r1c2 = value; return;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
case 1:
|
||||
switch (index.y)
|
||||
{
|
||||
case 0: r2c1 = value; return;
|
||||
case 1: r2c2 = value; return;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[int index, RowColumn direction]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case RowColumn.Row: return GetRow(index);
|
||||
case RowColumn.Column: return GetColumn(index);
|
||||
default: throw new ArgumentException($"Invalid direction {direction}.");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case RowColumn.Row: SetRow(index, value); break;
|
||||
case RowColumn.Column: SetColumn(index, value); break;
|
||||
default: throw new ArgumentException($"Invalid direction {direction}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Matrix2x2 Average(IEnumerable<Matrix2x2> vals)
|
||||
{
|
||||
Matrix2x2 sum = Zero;
|
||||
int count = 0;
|
||||
foreach (Matrix2x2 m in vals)
|
||||
{
|
||||
sum += m;
|
||||
count++;
|
||||
}
|
||||
return sum / count;
|
||||
}
|
||||
public static Matrix2x2 Lerp(Matrix2x2 a, Matrix2x2 b, double t, bool clamp = true) =>
|
||||
new Matrix2x2(MathE.Lerp(a.r1c1, b.r1c1, t, clamp), MathE.Lerp(a.r1c2, b.r1c2, t, clamp),
|
||||
MathE.Lerp(a.r2c1, b.r2c1, t, clamp), MathE.Lerp(a.r2c2, b.r2c2, t, clamp));
|
||||
public static Matrix2x2 Product(IEnumerable<Matrix2x2> vals)
|
||||
{
|
||||
bool any = false;
|
||||
Matrix2x2 result = One;
|
||||
foreach (Matrix2x2 m in vals)
|
||||
{
|
||||
any = true;
|
||||
result *= m;
|
||||
}
|
||||
return any ? result : Zero;
|
||||
}
|
||||
public static Matrix2x2 Sum(IEnumerable<Matrix2x2> vals)
|
||||
{
|
||||
Matrix2x2 result = Zero;
|
||||
foreach (Matrix2x2 m in vals) result += m;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static (double[] r1c1, double[] r1c2, double[] r2c1, double[] r2c2) SplitArray(IEnumerable<Matrix2x2> vals)
|
||||
{
|
||||
int count = vals.Count();
|
||||
double[] r1c1 = new double[count], r1c2 = new double[count],
|
||||
r2c1 = new double[count], r2c2 = new double[count];
|
||||
int index = 0;
|
||||
foreach (Matrix2x2 m in vals)
|
||||
{
|
||||
r1c1[index] = m.r1c1;
|
||||
r1c2[index] = m.r1c2;
|
||||
r2c1[index] = m.r2c1;
|
||||
r2c2[index] = m.r2c2;
|
||||
}
|
||||
return (r1c1, r1c2, r2c1, r2c2);
|
||||
}
|
||||
|
||||
public ListTuple<double> GetRow(int row)
|
||||
{
|
||||
double[] vals;
|
||||
switch (row)
|
||||
{
|
||||
case 0: vals = new double[] { r1c1, r1c2 }; break;
|
||||
case 1: vals = new double[] { r2c1, r2c2 }; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(row));
|
||||
}
|
||||
return new ListTuple<double>(vals);
|
||||
}
|
||||
public ListTuple<double> GetColumn(int column)
|
||||
{
|
||||
double[] vals;
|
||||
switch (column)
|
||||
{
|
||||
case 0: vals = new double[] { r1c1, r2c1 }; break;
|
||||
case 1: vals = new double[] { r1c2, r2c2 }; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(column));
|
||||
}
|
||||
return new ListTuple<double>(vals);
|
||||
}
|
||||
public void SetRow(int row, IEnumerable<double> vals)
|
||||
{
|
||||
int col = 0;
|
||||
foreach (double v in vals)
|
||||
{
|
||||
this[(row, col)] = v;
|
||||
col++;
|
||||
if (col >= 2) return;
|
||||
}
|
||||
}
|
||||
public void SetColumn(int column, IEnumerable<double> vals)
|
||||
{
|
||||
int row = 0;
|
||||
foreach (double v in vals)
|
||||
{
|
||||
this[(row, column)] = v;
|
||||
row++;
|
||||
if (row >= 2) return;
|
||||
}
|
||||
}
|
||||
public void SetRow(int row, ListTuple<double> vals) => SetRow(row, (IEnumerable<double>)vals);
|
||||
public void SetColumn(int row, ListTuple<double> vals) => SetColumn(row, (IEnumerable<double>)vals);
|
||||
|
||||
public double Determinant() => r1c1 * r2c2 - r1c2 * r2c1;
|
||||
|
||||
public Matrix2x2 Adjoint() =>
|
||||
new Matrix2x2( r2c2, -r1c2,
|
||||
-r2c1, r1c1);
|
||||
public Matrix2x2 Cofactor() =>
|
||||
new Matrix2x2( r2c2, -r2c1,
|
||||
-r1c2, r1c1);
|
||||
public Matrix2x2 Inverse()
|
||||
{
|
||||
double invDet = 1 / Determinant();
|
||||
return new Matrix2x2( r2c2 * invDet, -r1c2 * invDet,
|
||||
-r2c1 * invDet, r1c1 * invDet);
|
||||
}
|
||||
public Matrix2x2 Transpose() =>
|
||||
new Matrix2x2(r1c1, r2c1,
|
||||
r1c2, r2c2);
|
||||
public double Trace() => r1c1 + r2c2;
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return r1c1;
|
||||
yield return r1c2;
|
||||
yield return r2c1;
|
||||
yield return r2c2;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#if CS8_OR_GREATER
|
||||
public bool Equals(Matrix2x2? other) =>
|
||||
#else
|
||||
public bool Equals(Matrix2x2 other) =>
|
||||
#endif
|
||||
!(other is null) &&
|
||||
r1c1 == other.r1c1 && r1c2 == other.r1c2 &&
|
||||
r2c1 == other.r2c1 && r2c2 == other.r2c2;
|
||||
#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 Matrix2x2 otherMat) return Equals(otherMat);
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() =>
|
||||
(int)((uint)r1c1.GetHashCode() & 0xFF000000 |
|
||||
(uint)r1c2.GetHashCode() & 0x00FF0000 |
|
||||
(uint)r2c1.GetHashCode() & 0x0000FF00 |
|
||||
(uint)r2c2.GetHashCode() & 0x000000FF);
|
||||
public override string ToString() => ToStringHelper.MatrixToString(this, null);
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format) => ToStringHelper.MatrixToString(this, format);
|
||||
#else
|
||||
public string ToString(string format) => ToStringHelper.MatrixToString(this, format);
|
||||
#endif
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.MatrixToString(this, format);
|
||||
#else
|
||||
public string ToString(string format, IFormatProvider provider) => ToStringHelper.MatrixToString(this, format);
|
||||
#endif
|
||||
|
||||
public static Matrix2x2 operator +(Matrix2x2 a) =>
|
||||
new Matrix2x2(a.r1c1, a.r1c2,
|
||||
a.r2c1, a.r2c2);
|
||||
public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) =>
|
||||
new Matrix2x2(a.r1c1 + b.r1c1, a.r1c2 + b.r1c2,
|
||||
a.r2c1 + b.r2c1, a.r2c2 + b.r2c2);
|
||||
public static Matrix2x2 operator -(Matrix2x2 a) =>
|
||||
new Matrix2x2(-a.r1c1, -a.r1c2,
|
||||
-a.r2c1, -a.r2c2);
|
||||
public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) =>
|
||||
new Matrix2x2(a.r1c1 - b.r1c1, a.r1c2 - b.r1c2,
|
||||
a.r2c1 - b.r2c1, a.r2c2 - b.r2c2);
|
||||
public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) =>
|
||||
new Matrix2x2(a.r1c1 * b.r1c1 + a.r1c2 * b.r2c1, a.r1c1 * b.r1c2 + a.r1c2 * b.r2c2,
|
||||
a.r2c1 * b.r1c1 + a.r2c2 * b.r2c1, a.r2c1 * b.r1c2 + a.r2c2 * b.r2c2);
|
||||
public static Matrix2x2 operator *(Matrix2x2 a, double b) =>
|
||||
new Matrix2x2(a.r1c1 * b, a.r1c2 * b,
|
||||
a.r2c1 * b, a.r2c2 * b);
|
||||
public static Float2 operator *(Matrix2x2 a, Float2 b) =>
|
||||
new Float2(a.r1c1 * b.x + a.r1c2 * b.y,
|
||||
a.r2c1 * b.x + a.r2c2 * b.y);
|
||||
public static Matrix2x2 operator /(Matrix2x2 a, double b) =>
|
||||
new Matrix2x2(a.r1c1 / b, a.r1c2 / b,
|
||||
a.r2c1 / b, a.r2c2 / b);
|
||||
public static Matrix2x2 operator ~(Matrix2x2 a) => a.Inverse();
|
||||
}
|
||||
}
|
||||
8
Nerd_STF/Mathematics/Algebra/RowColumn.cs
Normal file
8
Nerd_STF/Mathematics/Algebra/RowColumn.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Nerd_STF.Mathematics.Algebra
|
||||
{
|
||||
public enum RowColumn
|
||||
{
|
||||
Row,
|
||||
Column
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public struct Angle : IComparable<Angle>,
|
||||
IEquatable<Angle>
|
||||
// Maybe move to .Numbers and add inheritance to INumber? Does this make sense?
|
||||
public readonly struct Angle : IComparable<Angle>,
|
||||
IEquatable<Angle>
|
||||
#if CS11_OR_GREATER
|
||||
,IPresets2d<Angle>,
|
||||
IFromTuple<Angle, (double, Angle.Unit)>
|
||||
,IFromTuple<Angle, (double, Angle.Units)>,
|
||||
IPresets2d<Angle>
|
||||
#endif
|
||||
{
|
||||
public static Angle Down => new Angle(0.75);
|
||||
@ -23,45 +23,29 @@ namespace Nerd_STF.Mathematics
|
||||
public static Angle Zero => new Angle(0);
|
||||
|
||||
#if CS11_OR_GREATER
|
||||
static Angle IPresets1d<Angle>.One => new Angle(1, Unit.Degrees);
|
||||
static Angle IPresets1d<Angle>.One => new Angle(1, Units.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 double Degrees => revTheta * 360;
|
||||
public double Gradians => revTheta * 400;
|
||||
public double Radians => revTheta * Constants.Tau;
|
||||
public double Revolutions => revTheta;
|
||||
|
||||
public Angle Complimentary => new Angle(0.25 - MathE.ModAbs(revTheta, 1));
|
||||
public Angle Supplimentary => new Angle(0.5 - MathE.ModAbs(revTheta, 1));
|
||||
public Angle Normalized => new Angle(MathE.ModAbs(revTheta, 1));
|
||||
public Angle Reflected => new Angle(MathE.ModAbs(-revTheta, 1));
|
||||
|
||||
private double revTheta;
|
||||
private readonly double revTheta;
|
||||
|
||||
public Angle(double theta, Unit unit)
|
||||
public Angle(double theta, Units 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;
|
||||
case Units.Revolutions: revTheta = theta; break;
|
||||
case Units.Degrees: revTheta = theta / 360; break;
|
||||
case Units.Radians: revTheta = theta / Constants.Tau; break;
|
||||
case Units.Gradians: revTheta = theta / 400; break;
|
||||
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit));
|
||||
}
|
||||
}
|
||||
@ -70,30 +54,64 @@ namespace Nerd_STF.Mathematics
|
||||
this.revTheta = revTheta;
|
||||
}
|
||||
|
||||
public double this[Unit unit]
|
||||
public double this[Units 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;
|
||||
case Units.Revolutions: return revTheta;
|
||||
case Units.Degrees: return revTheta * 360;
|
||||
case Units.Radians: return revTheta * Constants.Tau;
|
||||
case Units.Gradians: return revTheta * 400;
|
||||
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit));
|
||||
}
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
public static double Convert(double value, Units from, Units to)
|
||||
{
|
||||
switch (from)
|
||||
{
|
||||
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));
|
||||
}
|
||||
case Units.Revolutions:
|
||||
switch (to)
|
||||
{
|
||||
case Units.Revolutions: return value;
|
||||
case Units.Degrees: return value * 360;
|
||||
case Units.Radians: return value * 6.28318530718;
|
||||
case Units.Gradians: return value * 400;
|
||||
default: goto _fail;
|
||||
}
|
||||
case Units.Degrees:
|
||||
switch (to)
|
||||
{
|
||||
case Units.Revolutions: return value * 0.00277777777778;
|
||||
case Units.Degrees: return value;
|
||||
case Units.Radians: return value * 0.0174532925199;
|
||||
case Units.Gradians: return value * 1.11111111111;
|
||||
default: goto _fail;
|
||||
}
|
||||
case Units.Radians:
|
||||
switch (to)
|
||||
{
|
||||
case Units.Revolutions: return value * 0.159154943092;
|
||||
case Units.Degrees: return value * 57.2957795131;
|
||||
case Units.Radians: return value;
|
||||
case Units.Gradians: return value * 63.6619772368;
|
||||
default: goto _fail;
|
||||
}
|
||||
case Units.Gradians:
|
||||
switch (to)
|
||||
{
|
||||
case Units.Revolutions: return value * 0.0025;
|
||||
case Units.Degrees: return value * 0.9;
|
||||
case Units.Radians: return value * 0.0157079632679;
|
||||
case Units.Gradians: return value;
|
||||
default: goto _fail;
|
||||
}
|
||||
default: goto _fail;
|
||||
}
|
||||
_fail: throw new ArgumentException($"Invalid conversion: {from} -> {to}.");
|
||||
}
|
||||
|
||||
public static Angle Average(IEnumerable<Angle> angles)
|
||||
@ -109,8 +127,6 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
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<Angle> values) => Max(false, values);
|
||||
public static Angle Max(bool normalize, IEnumerable<Angle> values)
|
||||
{
|
||||
@ -172,7 +188,7 @@ namespace Nerd_STF.Mathematics
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static double[] SplitArray(Unit unit, IEnumerable<Angle> values)
|
||||
public static double[] SplitArray(Units unit, IEnumerable<Angle> values)
|
||||
{
|
||||
int count = values.Count();
|
||||
double[] angles = new double[count];
|
||||
@ -185,6 +201,8 @@ namespace Nerd_STF.Mathematics
|
||||
return angles;
|
||||
}
|
||||
|
||||
public Angle Coterminal(int turns) => new Angle(revTheta + turns);
|
||||
|
||||
public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta);
|
||||
public bool Equals(Angle other) => revTheta == other.revTheta;
|
||||
#if CS8_OR_GREATER
|
||||
@ -198,26 +216,26 @@ namespace Nerd_STF.Mathematics
|
||||
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);
|
||||
public override string ToString() => ToString(Units.Degrees, null);
|
||||
public string ToString(Units unit) => ToString(unit, null);
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(string? format) =>
|
||||
#else
|
||||
public string ToString(string format) =>
|
||||
#endif
|
||||
ToString(Unit.Degrees, format);
|
||||
ToString(Units.Degrees, format);
|
||||
#if CS8_OR_GREATER
|
||||
public string ToString(Unit unit, string? format)
|
||||
public string ToString(Units unit, string? format)
|
||||
#else
|
||||
public string ToString(Unit unit, string format)
|
||||
public string ToString(Units 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";
|
||||
case Units.Revolutions: return $"{revTheta.ToString(format)} rev";
|
||||
case Units.Degrees: return $"{(revTheta * 360).ToString(format)} deg";
|
||||
case Units.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad";
|
||||
case Units.Gradians: return $"{(revTheta * 400).ToString(format)} grad";
|
||||
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit));
|
||||
}
|
||||
}
|
||||
@ -234,12 +252,12 @@ namespace Nerd_STF.Mathematics
|
||||
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);
|
||||
public static implicit operator Angle((double, Units) tuple) => new Angle(tuple.Item1, tuple.Item2);
|
||||
#if CS11_OR_GREATER
|
||||
static implicit IFromTuple<Angle, (double, Unit)>.operator ValueTuple<double, Unit>(Angle angle) => (angle.revTheta, Unit.Revolutions);
|
||||
static implicit IFromTuple<Angle, (double, Units)>.operator ValueTuple<double, Units>(Angle angle) => (angle.revTheta, Units.Revolutions);
|
||||
#endif
|
||||
|
||||
public enum Unit
|
||||
public enum Units
|
||||
{
|
||||
Revolutions,
|
||||
Degrees,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerd_STF.Mathematics.Equations
|
||||
{
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Exceptions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -13,6 +12,7 @@ namespace Nerd_STF.Mathematics
|
||||
,IFromTuple<Float2, (double, double)>,
|
||||
IPresets2d<Float2>,
|
||||
IRoundable<Float2, Int2>,
|
||||
IRefRoundable<Float2>,
|
||||
ISplittable<Float2, (double[] Xs, double[] Ys)>
|
||||
#endif
|
||||
{
|
||||
@ -70,20 +70,22 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<double> this[string key]
|
||||
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 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'x': items[i] = x; break;
|
||||
case 'y': items[i] = y; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
@ -274,12 +276,16 @@ namespace Nerd_STF.Mathematics
|
||||
public static implicit operator Float2(PointF point) => new Float2(point.X, point.Y);
|
||||
public static implicit operator Float2(Size point) => new Float2(point.Width, point.Height);
|
||||
public static implicit operator Float2(SizeF size) => new Float2(size.Width, size.Height);
|
||||
public static implicit operator Float2(ListTuple<double> tuple) => new Float2(tuple[0], tuple[1]);
|
||||
public static implicit operator Float2(ListTuple<int> tuple) => new Float2(tuple[0], tuple[1]);
|
||||
public static implicit operator Float2((double, double) tuple) => new Float2(tuple.Item1, tuple.Item2);
|
||||
|
||||
public static explicit operator Point(Float2 group) => new Point((int)group.x, (int)group.y);
|
||||
public static implicit operator PointF(Float2 group) => new PointF((float)group.x, (float)group.y);
|
||||
public static explicit operator Size(Float2 group) => new Size((int)group.x, (int)group.y);
|
||||
public static implicit operator SizeF(Float2 group) => new SizeF((float)group.x, (float)group.y);
|
||||
public static implicit operator ListTuple<double>(Float2 group) => new ListTuple<double>(group.x, group.y);
|
||||
public static explicit operator ListTuple<int>(Float2 group) => new ListTuple<int>((int)group.x, (int)group.y);
|
||||
public static implicit operator ValueTuple<double, double>(Float2 group) => (group.x, group.y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Exceptions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics
|
||||
,IFromTuple<Float3, (double, double, double)>,
|
||||
IPresets2d<Float3>,
|
||||
IRoundable<Float3, Int3>,
|
||||
IRefRoundable<Float3>,
|
||||
ISplittable<Float3, (double[] Xs, double[] Ys, double[] Zs)>
|
||||
#endif
|
||||
{
|
||||
@ -75,21 +75,23 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<double> this[string key]
|
||||
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 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'z': yield return z; 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
|
||||
{
|
||||
@ -296,8 +298,12 @@ namespace Nerd_STF.Mathematics
|
||||
public static implicit operator Float3(Int2 ints) => new Float3(ints.x, ints.y, 0);
|
||||
public static implicit operator Float3(Int3 ints) => new Float3(ints.x, ints.y, ints.z);
|
||||
public static explicit operator Float3(Int4 ints) => new Float3(ints.x, ints.y, ints.z);
|
||||
public static implicit operator Float3(ListTuple<double> tuple) => new Float3(tuple[0], tuple[1], tuple[2]);
|
||||
public static implicit operator Float3(ListTuple<int> tuple) => new Float3(tuple[0], tuple[1], tuple[2]);
|
||||
public static implicit operator Float3((double, double, double) tuple) => new Float3(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
|
||||
public static implicit operator ListTuple<double>(Float3 group) => new ListTuple<double>(group.x, group.y, group.z);
|
||||
public static explicit operator ListTuple<int>(Float3 group) => new ListTuple<int>((int)group.x, (int)group.y, (int)group.z);
|
||||
public static implicit operator ValueTuple<double, double, double>(Float3 group) => (group.x, group.y, group.z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
|
||||
namespace Nerd_STF.Mathematics
|
||||
@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics
|
||||
,IFromTuple<Float4, (double, double, double, double)>,
|
||||
IPresets4d<Float4>,
|
||||
IRoundable<Float4, Int4>,
|
||||
IRefRoundable<Float4>,
|
||||
ISplittable<Float4, (double[] Ws, double[] Xs, double[] Ys, double[] Zs)>
|
||||
#endif
|
||||
{
|
||||
@ -81,22 +81,24 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<double> this[string key]
|
||||
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': yield return w; break;
|
||||
case 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'z': yield return z; break;
|
||||
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
|
||||
{
|
||||
@ -316,8 +318,12 @@ namespace Nerd_STF.Mathematics
|
||||
public static implicit operator Float4(Int4 ints) => new Float4(ints.w, ints.x, ints.y, ints.z);
|
||||
public static implicit operator Float4(Float2 floats) => new Float4(0, floats.x, floats.y, 0);
|
||||
public static implicit operator Float4(Float3 floats) => new Float4(0, floats.x, floats.y, floats.z);
|
||||
public static implicit operator Float4(ListTuple<double> tuple) => new Float4(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
public static implicit operator Float4(ListTuple<int> tuple) => new Float4(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
public static implicit operator Float4((double, double, double, double) tuple) => new Float4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
|
||||
public static implicit operator ListTuple<double>(Float4 group) => new ListTuple<double>(group.w, group.x, group.y, group.z);
|
||||
public static explicit operator ListTuple<int>(Float4 group) => new ListTuple<int>((int)group.w, (int)group.x, (int)group.y, (int)group.z);
|
||||
public static implicit operator ValueTuple<double, double, double, double>(Float4 group) => (group.w, group.x, group.y, group.z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IInterpolable<TSelf>
|
||||
where TSelf : IInterpolable<TSelf>
|
||||
@ -1,14 +1,15 @@
|
||||
using System;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>,
|
||||
IEnumerable<TItem>,
|
||||
IEquatable<TSelf>
|
||||
#if CS11_OR_GREATER
|
||||
,IInterpolable<TSelf>,
|
||||
, IInterpolable<TSelf>,
|
||||
ISimpleMathOperations<TSelf>,
|
||||
IVectorOperations<TSelf>
|
||||
#endif
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IPresets1d<TSelf> where TSelf : IPresets1d<TSelf>
|
||||
{
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IPresets2d<TSelf> : IPresets1d<TSelf>
|
||||
where TSelf : IPresets2d<TSelf>
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IPresets3d<TSelf> : IPresets2d<TSelf>
|
||||
where TSelf : IPresets3d<TSelf>
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IPresets4d<TSelf> : IPresets3d<TSelf>
|
||||
where TSelf : IPresets4d<TSelf>
|
||||
12
Nerd_STF/Mathematics/IRefRoundable.cs
Normal file
12
Nerd_STF/Mathematics/IRefRoundable.cs
Normal file
@ -0,0 +1,12 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IRefRoundable<TSelf>
|
||||
where TSelf : IRefRoundable<TSelf>
|
||||
{
|
||||
static abstract void Ceiling(ref TSelf val);
|
||||
static abstract void Floor(ref TSelf val);
|
||||
static abstract void Round(ref TSelf val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1,5 +1,5 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface IRoundable<TSelf> : IRoundable<TSelf, TSelf>
|
||||
where TSelf : IRoundable<TSelf> { }
|
||||
@ -2,12 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerd_STF.Abstract
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface ISimpleMathOperations<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>,
|
||||
ISubtractionOperators<TSelf, TSelf, TSelf>,
|
||||
IMultiplyOperators<TSelf, TSelf, TSelf>,
|
||||
IDivisionOperators<TSelf, TSelf, TSelf>
|
||||
IMultiplyOperators<TSelf, TSelf, TSelf>
|
||||
where TSelf : ISimpleMathOperations<TSelf>
|
||||
{
|
||||
static abstract TSelf Product(IEnumerable<TSelf> vals);
|
||||
@ -1,5 +1,5 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -69,20 +69,22 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<int> this[string key]
|
||||
public ListTuple<int> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
int[] items = new int[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'x': items[i] = x; break;
|
||||
case 'y': items[i] = y; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<int>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
@ -248,12 +250,16 @@ namespace Nerd_STF.Mathematics
|
||||
public static explicit operator Int2(PointF point) => new Int2((int)point.X, (int)point.Y);
|
||||
public static implicit operator Int2(Size size) => new Int2(size.Width, size.Height);
|
||||
public static explicit operator Int2(SizeF size) => new Int2((int)size.Width, (int)size.Height);
|
||||
public static explicit operator Int2(ListTuple<double> tuple) => new Int2((int)tuple[0], (int)tuple[1]);
|
||||
public static implicit operator Int2(ListTuple<int> tuple) => new Int2(tuple[0], tuple[1]);
|
||||
public static implicit operator Int2((int, int) tuple) => new Int2(tuple.Item1, tuple.Item2);
|
||||
|
||||
public static implicit operator Point(Int2 group) => new Point(group.x, group.y);
|
||||
public static explicit operator PointF(Int2 group) => new PointF(group.x, group.y);
|
||||
public static implicit operator Size(Int2 group) => new Size(group.x, group.y);
|
||||
public static explicit operator SizeF(Int2 group) => new SizeF(group.x, group.y);
|
||||
public static implicit operator ListTuple<double>(Int2 group) => new ListTuple<double>(group.x, group.y);
|
||||
public static implicit operator ListTuple<int>(Int2 group) => new ListTuple<int>(group.x, group.y);
|
||||
public static implicit operator ValueTuple<int, int>(Int2 group) => (group.x, group.y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -74,21 +74,23 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<int> this[string key]
|
||||
public ListTuple<int> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
int[] items = new int[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'z': yield return z; 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<int>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
@ -264,8 +266,12 @@ namespace Nerd_STF.Mathematics
|
||||
public static explicit operator Int3(Float2 floats) => new Int3((int)floats.x, (int)floats.y, 0);
|
||||
public static explicit operator Int3(Float3 floats) => new Int3((int)floats.x, (int)floats.y, (int)floats.z);
|
||||
public static explicit operator Int3(Float4 floats) => new Int3((int)floats.x, (int)floats.y, (int)floats.z);
|
||||
public static explicit operator Int3(ListTuple<double> tuple) => new Int3((int)tuple[0], (int)tuple[1], (int)tuple[2]);
|
||||
public static implicit operator Int3(ListTuple<int> tuple) => new Int3(tuple[0], tuple[1], tuple[2]);
|
||||
public static implicit operator Int3((int, int, int) tuple) => new Int3(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
|
||||
public static implicit operator ListTuple<double>(Int3 group) => new ListTuple<double>(group.x, group.y, group.z);
|
||||
public static implicit operator ListTuple<int>(Int3 group) => new ListTuple<int>(group.x, group.y, group.z);
|
||||
public static implicit operator ValueTuple<int, int, int>(Int3 group) => (group.x, group.y, group.z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Mathematics.Algebra;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -80,22 +80,24 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<int> this[string key]
|
||||
public ListTuple<int> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
int[] items = new int[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'w': yield return w; break;
|
||||
case 'x': yield return x; break;
|
||||
case 'y': yield return y; break;
|
||||
case 'z': yield return z; break;
|
||||
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<int>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
@ -276,8 +278,12 @@ namespace Nerd_STF.Mathematics
|
||||
public static explicit operator Int4(Float2 floats) => new Int4(0, (int)floats.x, (int)floats.y, 0);
|
||||
public static explicit operator Int4(Float3 floats) => new Int4(0, (int)floats.x, (int)floats.y, (int)floats.z);
|
||||
public static explicit operator Int4(Float4 floats) => new Int4((int)floats.w, (int)floats.x, (int)floats.y, (int)floats.z);
|
||||
public static explicit operator Int4(ListTuple<double> tuple) => new Int4((int)tuple[0], (int)tuple[1], (int)tuple[2], (int)tuple[3]);
|
||||
public static implicit operator Int4(ListTuple<int> tuple) => new Int4(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
public static implicit operator Int4((int, int, int, int) tuple) => new Int4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
|
||||
public static implicit operator ListTuple<double>(Int4 group) => new ListTuple<double>(group.w, group.x, group.y, group.z);
|
||||
public static implicit operator ListTuple<int>(Int4 group) => new ListTuple<int>(group.w, group.x, group.y, group.z);
|
||||
public static implicit operator ValueTuple<int, int, int, int>(Int4 group) => (group.w, group.x, group.y, group.z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using Nerd_STF.Abstract;
|
||||
using Nerd_STF.Helpers;
|
||||
using Nerd_STF.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@ -474,7 +473,8 @@ namespace Nerd_STF.Mathematics.Numbers
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() =>
|
||||
(int)(((uint)num.GetHashCode() & 0xFFFF0000u) | ((uint)den.GetHashCode() & 0x0000FFFFu));
|
||||
(int)((uint)num.GetHashCode() & 0xFFFF0000 |
|
||||
(uint)den.GetHashCode() & 0x0000FFFF);
|
||||
#if CS8_OR_GREATER
|
||||
public override string ToString() => ToString(null, null);
|
||||
public string ToString(string? format) => ToString(format, null);
|
||||
|
||||
@ -3,11 +3,12 @@
|
||||
<!-- General stuff -->
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;net471;netcoreapp3.0;net5.0;net7.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<DebugType>portable</DebugType>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<CheckNotRecommendedTargetFramework>false</CheckNotRecommendedTargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- NuGet package customization. -->
|
||||
@ -141,6 +142,10 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel
|
||||
<!-- Mostly used to reference system packages that are not included in this version of .NET Standard. -->
|
||||
<!-- TODO: Maybe this isn't good practice, and we should define environment variables for specific features (tuples, drawing, etc) instead? -->
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.1'">
|
||||
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
@ -156,23 +161,35 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel
|
||||
<!-- PropertyGroup customization based on framework. -->
|
||||
<!-- Used to define environment variables based on features the framework supports. -->
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
|
||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||
<Nullable>enable</Nullable>
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net471'">
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp3.0'">
|
||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER</DefineConstants>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net5.0'">
|
||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER</DefineConstants>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net7.0'">
|
||||
<DefineConstants>$(DefineConstants);CS10_OR_GREATER;CS11_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;CS10_OR_GREATER;CS11_OR_GREATER</DefineConstants>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.1'">
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
|
||||
<DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER</DefineConstants>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user