363 lines
14 KiB
C#
363 lines
14 KiB
C#
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[] r0c0, double[] r0c1, double[] r1c0, double[] r1c1)>,
|
|
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 r0c0, r0c1,
|
|
r1c0, r1c1;
|
|
|
|
public Matrix2x2()
|
|
{
|
|
r0c0 = 0; r0c1 = 0;
|
|
r1c0 = 0; r1c1 = 0;
|
|
}
|
|
public Matrix2x2(Matrix2x2 copy)
|
|
{
|
|
r0c0 = copy.r0c0; r0c1 = copy.r0c1;
|
|
r1c0 = copy.r1c0; r1c1 = copy.r1c1;
|
|
}
|
|
public Matrix2x2(double r0c0, double r0c1, double r1c0, double r1c1)
|
|
{
|
|
this.r0c0 = r0c0;
|
|
this.r0c1 = r0c1;
|
|
this.r1c0 = r1c0;
|
|
this.r1c1 = r1c1;
|
|
}
|
|
/// <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 = false)
|
|
{
|
|
if (byRows) // Collection of rows ([c, r])
|
|
{
|
|
r0c0 = vals[0, 0]; r0c1 = vals[1, 0];
|
|
r1c0 = vals[0, 1]; r1c1 = vals[1, 1];
|
|
}
|
|
else // Collection of columns ([r, c])
|
|
{
|
|
r0c0 = vals[0, 0]; r0c1 = vals[0, 1];
|
|
r1c0 = vals[1, 0]; r1c1 = 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 = false)
|
|
{
|
|
MatrixHelper.SetMatrixValues(this, vals, byRows);
|
|
}
|
|
/// <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 = false)
|
|
{
|
|
MatrixHelper.SetMatrixValues(this, vals, byRows);
|
|
}
|
|
/// <param name="byRows"><see langword="true"/> if the fill goes through columns for each row, <see langword="false"/> if the fill goes through rows for each column.</param>
|
|
public Matrix2x2(Fill<double> fill, bool byRows = false)
|
|
{
|
|
if (byRows)
|
|
{
|
|
r0c0 = fill(0); r0c1 = fill(2);
|
|
r1c0 = fill(1); r1c1 = fill(3);
|
|
}
|
|
else
|
|
{
|
|
r0c0 = fill(0); r0c1 = fill(1);
|
|
r1c0 = fill(2); r1c1 = fill(3);
|
|
}
|
|
}
|
|
/// <param name="byRows"><see langword="true"/> if the fill is a collection of rows (form [c, r]), <see langword="false"/> if the fill is a collection of columns (form [r, c]).</param>
|
|
public Matrix2x2(Fill2d<double> fill, bool byRows = false)
|
|
{
|
|
if (byRows)
|
|
{
|
|
r0c0 = fill(0, 0); r0c1 = fill(1, 0);
|
|
r1c0 = fill(0, 1); r1c1 = fill(1, 1);
|
|
}
|
|
else
|
|
{
|
|
r0c0 = fill(0, 0); r0c1 = fill(0, 1);
|
|
r1c0 = fill(1, 0); r1c1 = fill(1, 1);
|
|
}
|
|
}
|
|
|
|
public double this[int r, int c]
|
|
{
|
|
get
|
|
{
|
|
switch (r)
|
|
{
|
|
case 0:
|
|
switch (c)
|
|
{
|
|
case 0: return r0c0;
|
|
case 1: return r0c1;
|
|
default: throw new ArgumentOutOfRangeException(nameof(c));
|
|
}
|
|
case 1:
|
|
switch (c)
|
|
{
|
|
case 0: return r1c0;
|
|
case 1: return r1c1;
|
|
default: throw new ArgumentOutOfRangeException(nameof(c));
|
|
}
|
|
default: throw new ArgumentOutOfRangeException(nameof(r));
|
|
}
|
|
}
|
|
set
|
|
{
|
|
switch (r)
|
|
{
|
|
case 0:
|
|
switch (c)
|
|
{
|
|
case 0: r0c0 = value; return;
|
|
case 1: r0c1 = value; return;
|
|
default: throw new ArgumentOutOfRangeException(nameof(c));
|
|
}
|
|
case 1:
|
|
switch (c)
|
|
{
|
|
case 0: r1c0 = value; return;
|
|
case 1: r1c1 = value; return;
|
|
default: throw new ArgumentOutOfRangeException(nameof(c));
|
|
}
|
|
default: throw new ArgumentOutOfRangeException(nameof(r));
|
|
}
|
|
}
|
|
}
|
|
public double this[Int2 index]
|
|
{
|
|
get => this[index.x, index.y];
|
|
set => this[index.x, index.y] = value;
|
|
}
|
|
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.r0c0, b.r0c0, t, clamp), MathE.Lerp(a.r0c1, b.r0c1, t, clamp),
|
|
MathE.Lerp(a.r1c0, b.r1c0, t, clamp), MathE.Lerp(a.r1c1, b.r1c1, 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[] r0c0, double[] r0c1, double[] r1c0, double[] r1c1) SplitArray(IEnumerable<Matrix2x2> vals)
|
|
{
|
|
int count = vals.Count();
|
|
double[] r0c0 = new double[count], r0c1 = new double[count],
|
|
r1c0 = new double[count], r1c1 = new double[count];
|
|
int index = 0;
|
|
foreach (Matrix2x2 m in vals)
|
|
{
|
|
r0c0[index] = m.r0c0; r0c1[index] = m.r0c1;
|
|
r1c0[index] = m.r1c0; r1c1[index] = m.r1c1;
|
|
}
|
|
return (r0c0, r0c1,
|
|
r1c0, r1c1);
|
|
}
|
|
|
|
public ListTuple<double> GetRow(int row)
|
|
{
|
|
double[] vals;
|
|
switch (row)
|
|
{
|
|
case 0: vals = new double[] { r0c0, r0c1 }; break;
|
|
case 1: vals = new double[] { r1c0, r1c1 }; 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[] { r0c0, r1c0 }; break;
|
|
case 1: vals = new double[] { r0c1, r1c1 }; break;
|
|
default: throw new ArgumentOutOfRangeException(nameof(column));
|
|
}
|
|
return new ListTuple<double>(vals);
|
|
}
|
|
public void SetRow(int row, IEnumerable<double> vals) => MatrixHelper.SetRow(this, row, vals);
|
|
public void SetColumn(int column, IEnumerable<double> vals) => MatrixHelper.SetColumn(this, column, vals);
|
|
public void SetRow(int row, ListTuple<double> vals)
|
|
{
|
|
switch (row)
|
|
{
|
|
case 0: r0c0 = vals[0]; r0c1 = vals[1]; break;
|
|
case 1: r1c0 = vals[0]; r1c1 = vals[1]; break;
|
|
default: throw new ArgumentOutOfRangeException(nameof(row));
|
|
}
|
|
}
|
|
public void SetColumn(int column, ListTuple<double> vals)
|
|
{
|
|
switch (column)
|
|
{
|
|
case 0: r0c0 = vals[0]; r1c0 = vals[1]; break;
|
|
case 1: r0c1 = vals[0]; r1c1 = vals[1]; break;
|
|
default: throw new ArgumentOutOfRangeException(nameof(column));
|
|
}
|
|
}
|
|
|
|
public double Determinant() => r0c0 * r1c1 - r0c1 * r1c0;
|
|
|
|
public Matrix2x2 Adjoint() =>
|
|
new Matrix2x2( r1c1, -r0c1,
|
|
-r1c0, r0c0);
|
|
public Matrix2x2 Cofactor() =>
|
|
new Matrix2x2( r1c1, -r1c0,
|
|
-r0c1, r0c0);
|
|
public Matrix2x2 Inverse()
|
|
{
|
|
double invDet = 1 / Determinant();
|
|
return new Matrix2x2( r1c1 * invDet, -r0c1 * invDet,
|
|
-r1c0 * invDet, r0c0 * invDet);
|
|
}
|
|
public Matrix2x2 Transpose() =>
|
|
new Matrix2x2(r0c0, r1c0,
|
|
r0c1, r1c1);
|
|
public double Trace() => r0c0 + r1c1;
|
|
|
|
public IEnumerator<double> GetEnumerator()
|
|
{
|
|
yield return r0c0; yield return r0c1;
|
|
yield return r1c0; yield return r1c1;
|
|
}
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
#if CS8_OR_GREATER
|
|
public bool Equals(Matrix2x2? other) =>
|
|
#else
|
|
public bool Equals(Matrix2x2 other) =>
|
|
#endif
|
|
!(other is null) &&
|
|
r0c0 == other.r0c0 && r0c1 == other.r0c1 &&
|
|
r1c0 == other.r1c0 && r1c1 == other.r1c1;
|
|
#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)r0c0.GetHashCode() & 0xFF000000 |
|
|
(uint)r0c1.GetHashCode() & 0x00FF0000 |
|
|
(uint)r1c0.GetHashCode() & 0x0000FF00 |
|
|
(uint)r1c1.GetHashCode() & 0x000000FF);
|
|
public override string ToString() => ToStringHelper.MatrixToString(this, null);
|
|
#if CS8_OR_GREATER
|
|
public string ToString(string? format) => ToStringHelper.MatrixToString(this, format);
|
|
public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.MatrixToString(this, format);
|
|
#else
|
|
public string ToString(string format) => ToStringHelper.MatrixToString(this, format);
|
|
public string ToString(string format, IFormatProvider provider) => ToStringHelper.MatrixToString(this, format);
|
|
#endif
|
|
|
|
public static Matrix2x2 operator +(Matrix2x2 a) =>
|
|
new Matrix2x2(a.r0c0, a.r0c1,
|
|
a.r1c0, a.r1c1);
|
|
public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) =>
|
|
new Matrix2x2(a.r0c0 + b.r0c0, a.r0c1 + b.r0c1,
|
|
a.r1c0 + b.r1c0, a.r1c1 + b.r1c1);
|
|
public static Matrix2x2 operator -(Matrix2x2 a) =>
|
|
new Matrix2x2(-a.r0c0, -a.r0c1,
|
|
-a.r1c0, -a.r1c1);
|
|
public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) =>
|
|
new Matrix2x2(a.r0c0 - b.r0c0, a.r0c1 - b.r0c1,
|
|
a.r1c0 - b.r1c0, a.r1c1 - b.r1c1);
|
|
public static Matrix2x2 operator *(Matrix2x2 a, double b) =>
|
|
new Matrix2x2(a.r0c0 * b, a.r0c1 * b,
|
|
a.r1c0 * b, a.r1c1 * b);
|
|
public static Float2 operator *(Matrix2x2 a, Float2 b) =>
|
|
new Float2(a.r0c0 * b.x + a.r0c1 * b.y,
|
|
a.r1c0 * b.x + a.r1c1 * b.y);
|
|
public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) =>
|
|
new Matrix2x2(a.r0c0 * b.r0c0 + a.r0c1 * b.r1c0, a.r0c0 * b.r0c1 + a.r0c1 * b.r1c1,
|
|
a.r1c0 * b.r0c0 + a.r1c1 * b.r1c0, a.r1c0 * b.r0c1 + a.r1c1 * b.r1c1);
|
|
public static Matrix2x2 operator /(Matrix2x2 a, double b) =>
|
|
new Matrix2x2(a.r0c0 / b, a.r0c1 / b,
|
|
a.r1c0 / b, a.r1c1 / b);
|
|
public static Matrix2x2 operator ^(Matrix2x2 a, Matrix2x2 b) =>
|
|
new Matrix2x2(a.r0c0 * b.r0c0, a.r0c1 * b.r0c1,
|
|
a.r1c0 * b.r1c0, a.r1c1 * b.r1c1);
|
|
public static Matrix2x2 operator ~(Matrix2x2 a) => a.Inverse();
|
|
public static bool operator ==(Matrix2x2 a, Matrix2x2 b) => a.Equals(b);
|
|
public static bool operator !=(Matrix2x2 a, Matrix2x2 b) => !a.Equals(b);
|
|
|
|
public static explicit operator Matrix2x2(Matrix mat) =>
|
|
new Matrix2x2(mat.TryGet(0, 0), mat.TryGet(0, 1),
|
|
mat.TryGet(1, 0), mat.TryGet(1, 1));
|
|
public static explicit operator Matrix2x2(Matrix3x3 mat) =>
|
|
new Matrix2x2(mat.r0c0, mat.r0c1,
|
|
mat.r1c0, mat.r1c1);
|
|
public static explicit operator Matrix2x2(Matrix4x4 mat) =>
|
|
new Matrix2x2(mat.r0c0, mat.r0c1,
|
|
mat.r1c0, mat.r1c1);
|
|
}
|
|
}
|