From f057215e6e157a1d9564909b6b2f503f3ac20cf7 Mon Sep 17 00:00:00 2001 From: Dominic Beger Date: Sun, 1 May 2016 00:02:06 +0200 Subject: [PATCH] Change Sharpmath.Geometry classes to structs and revise the whole architecture --- SharpMath.Tests/MatrixTest.cs | 165 ++++- SharpMath.Tests/VectorTest.cs | 65 +- SharpMath/{Algorithm.cs => Algorithms.cs} | 47 -- SharpMath/Equations/LinearEquationSystem.cs | 10 +- SharpMath/Geometry/IMatrix.cs | 10 + SharpMath/Geometry/ISquareMatrix.cs | 16 + SharpMath/Geometry/IVector.cs | 37 ++ SharpMath/Geometry/Line2D.cs | 31 +- SharpMath/Geometry/Line3D.cs | 21 +- SharpMath/Geometry/Matrix.cs | 628 ------------------ SharpMath/Geometry/Matrix1x1.cs | 305 +++++++++ SharpMath/Geometry/Matrix1x3.cs | 95 +++ SharpMath/Geometry/Matrix1x4.cs | 102 +++ SharpMath/Geometry/Matrix2x2.cs | 341 ++++++++++ SharpMath/Geometry/Matrix3x1.cs | 95 +++ SharpMath/Geometry/Matrix3x3.cs | 364 ++++++++-- SharpMath/Geometry/Matrix4x1.cs | 102 +++ SharpMath/Geometry/Matrix4x4.cs | 452 +++++++++++-- SharpMath/Geometry/MatrixEnumerator.cs | 39 ++ SharpMath/Geometry/MatrixUtils.cs | 363 ++++++++++ SharpMath/Geometry/Point.cs | 321 --------- SharpMath/Geometry/Point2D.cs | 142 ++-- SharpMath/Geometry/Point3D.cs | 155 +++-- SharpMath/Geometry/Polygon.cs | 12 +- SharpMath/Geometry/SquareMatrix.cs | 155 ----- SharpMath/Geometry/VariableMatrix.cs | 62 ++ SharpMath/Geometry/Vector.cs | 696 -------------------- SharpMath/Geometry/Vector2.cs | 317 ++++++--- SharpMath/Geometry/Vector3.cs | 251 ++++--- SharpMath/Geometry/Vector4.cs | 172 +++-- SharpMath/Geometry/VectorExtensions.cs | 41 -- SharpMath/SharpMath.csproj | 21 +- 32 files changed, 3165 insertions(+), 2468 deletions(-) rename SharpMath/{Algorithm.cs => Algorithms.cs} (58%) create mode 100644 SharpMath/Geometry/IMatrix.cs create mode 100644 SharpMath/Geometry/ISquareMatrix.cs create mode 100644 SharpMath/Geometry/IVector.cs delete mode 100644 SharpMath/Geometry/Matrix.cs create mode 100644 SharpMath/Geometry/Matrix1x1.cs create mode 100644 SharpMath/Geometry/Matrix1x3.cs create mode 100644 SharpMath/Geometry/Matrix1x4.cs create mode 100644 SharpMath/Geometry/Matrix2x2.cs create mode 100644 SharpMath/Geometry/Matrix3x1.cs create mode 100644 SharpMath/Geometry/Matrix4x1.cs create mode 100644 SharpMath/Geometry/MatrixEnumerator.cs create mode 100644 SharpMath/Geometry/MatrixUtils.cs delete mode 100644 SharpMath/Geometry/Point.cs delete mode 100644 SharpMath/Geometry/SquareMatrix.cs create mode 100644 SharpMath/Geometry/VariableMatrix.cs delete mode 100644 SharpMath/Geometry/Vector.cs delete mode 100644 SharpMath/Geometry/VectorExtensions.cs diff --git a/SharpMath.Tests/MatrixTest.cs b/SharpMath.Tests/MatrixTest.cs index 84bcd46..704b327 100644 --- a/SharpMath.Tests/MatrixTest.cs +++ b/SharpMath.Tests/MatrixTest.cs @@ -12,37 +12,76 @@ public class MatrixTest [TestMethod] public void CanMultiplyMatrices() { - var firstMatrix = new Matrix(2, 3); - var secondMatrix = new Matrix(3, 2); + var firstMatrix = new Matrix1x3(); + var secondMatrix = new Matrix3x1(); firstMatrix[0, 0] = 1; firstMatrix[0, 1] = 2; firstMatrix[0, 2] = 3; - firstMatrix[1, 0] = 3; - firstMatrix[1, 1] = 1; - firstMatrix[1, 2] = 1; secondMatrix[0, 0] = 2; - secondMatrix[0, 1] = 1; secondMatrix[1, 0] = 1; - secondMatrix[1, 1] = 2; secondMatrix[2, 0] = 2; - secondMatrix[2, 1] = 1; - var matrixProduct = Matrix.Multiply(firstMatrix, secondMatrix); + var matrixProduct = MatrixUtils.Multiply(firstMatrix, secondMatrix); Assert.AreEqual(matrixProduct[0, 0], 10); - Assert.AreEqual(matrixProduct[0, 1], 8); - Assert.AreEqual(matrixProduct[1, 0], 9); - Assert.AreEqual(matrixProduct[1, 1], 6); + + // -------------------------------- + + var thirdMatrix = new Matrix3x3 + { + [0, 0] = 2, + [0, 1] = 5, + [0, 2] = 2, + [1, 0] = 3, + [1, 1] = -3, + [1, 2] = 1, + [2, 0] = 1, + [2, 1] = 4, + [2, 2] = -4 + }; + + var fourthMatrix = new Matrix3x3 + { + [0, 0] = -4, + [0, 1] = 2.5, + [0, 2] = 3, + [1, 0] = 5, + [1, 1] = 6, + [1, 2] = 4, + [2, 0] = 9, + [2, 1] = 10, + [2, 2] = -9 + }; + + /* + 35.000 55.000 8.000 + -18.000 -0.500 -12.000 + -20.000 -13.500 55.000 + */ + var resultMatrix = new Matrix3x3() + { + [0, 0] = 35, + [0, 1] = 55, + [0, 2] = 8, + [1, 0] = -18, + [1, 1] = -0.5, + [1, 2] = -12, + [2, 0] = -20, + [2, 1] = -13.5, + [2, 2] = 55 + }; + + Assert.AreEqual(resultMatrix, MatrixUtils.Multiply(thirdMatrix, fourthMatrix)); } [TestMethod] public void CanCalculateDeterminant() { - var firstMatrix = new SquareMatrix(1) {[0, 0] = 2}; + var firstMatrix = new Matrix1x1() {[0, 0] = 2}; Assert.AreEqual(2, firstMatrix.Determinant); - var secondMatrix = new SquareMatrix(2) + var secondMatrix = new Matrix2x2() { [0, 0] = 8, [0, 1] = 3, @@ -51,7 +90,7 @@ public void CanCalculateDeterminant() }; Assert.AreEqual(4, secondMatrix.Determinant); - var thirdMatrix = new SquareMatrix(3) + var thirdMatrix = new Matrix3x3 { [0, 0] = 2, [0, 1] = 5, @@ -64,10 +103,9 @@ public void CanCalculateDeterminant() [2, 2] = -4 }; - Assert.AreEqual(111, thirdMatrix.Determinant); - var fourthMatrix = new SquareMatrix(3) + var fourthMatrix = new Matrix3x3 { [0, 0] = -4, [0, 1] = 2.5, @@ -82,7 +120,7 @@ public void CanCalculateDeterminant() Assert.AreEqual(566.5, fourthMatrix.Determinant); - var fifthMatrix = new SquareMatrix(4) + var fifthMatrix = new Matrix4x4 { [0, 0] = 1, [0, 1] = 0, @@ -111,5 +149,96 @@ public void CanGetStringRepresentation() var matrix = Matrix4x4.View(Vector3.Zero, Vector3.Forward, Vector3.Up); Debug.Print(matrix.ToString()); } + + [TestMethod] + public void CanDetermineIfMatrixIsDiagonal() + { + var matrix = new Matrix3x3() + { + M11 = 1, + M22 = 1, + M33 = 1 + }; + Assert.IsTrue(matrix.IsDiagonal); + + var secondMatrix = new Matrix4x4() + { + M11 = 1, + M22 = 1, + M33 = 1, + M44 = 1, + }; + Assert.IsTrue(secondMatrix.IsDiagonal); + + var thirdMatrix = new Matrix4x4() + { + M11 = 1, + M22 = 1, + M33 = 1, + M44 = 1, + M14 = 1, + }; + Assert.IsFalse(thirdMatrix.IsDiagonal); + + var fourthMatrix = new Matrix4x4() + { + M11 = 1, + M22 = 1, + M33 = 1, + }; + Assert.IsFalse(fourthMatrix.IsDiagonal); + } + + [TestMethod] + public void CanDetermineIfMatrixIsTriangle() + { + var matrix = new Matrix3x3() + { + M11 = 1, + M22 = 2, + M33 = 3, + + M12 = 3, + M13 = 4, + M23 = 7, + }; + Assert.IsTrue(matrix.IsTriangle); + + var secondMatrix = new Matrix4x4() + { + M11 = 1, + M22 = 2, + M33 = 3, + M44 = 8, + + M12 = 3, + M13 = 4, + M14 = 7, + M23 = 2, + M24 = 3, + M34 = 9 + }; + Assert.IsTrue(secondMatrix.IsTriangle); + + var thirdMatrix = new Matrix4x4() + { + M11 = 5, + M22 = 7, + M33 = 4, + M44 = 3, + M14 = 2, + M31 = 9, + }; + Assert.IsFalse(thirdMatrix.IsTriangle); + + var fourthMatrix = new Matrix4x4() + { + M11 = 1, + M22 = 1, + M33 = 1, + M44 = 1 + }; + Assert.IsTrue(fourthMatrix.IsTriangle); + } } } \ No newline at end of file diff --git a/SharpMath.Tests/VectorTest.cs b/SharpMath.Tests/VectorTest.cs index 31ca9d2..8907c98 100644 --- a/SharpMath.Tests/VectorTest.cs +++ b/SharpMath.Tests/VectorTest.cs @@ -46,10 +46,10 @@ public void CanCalculateLength() [TestMethod] public void CanCalculateScalarProduct() { - Assert.AreEqual(0, Vector2.UnitX.ScalarProduct(Vector2.UnitY)); - Assert.AreEqual(20, Vector.ScalarProduct(new Vector2(2, 4), new Vector2(4, 3))); - Assert.AreEqual(0, Vector.ScalarProduct(Vector3.Forward, Vector3.Up)); - Assert.AreEqual(8, Vector.ScalarProduct(new Vector3(2, 3, 1), new Vector3(-1, 2, 4))); + Assert.AreEqual(0, Vector2.UnitX.DotProduct(Vector2.UnitY)); + Assert.AreEqual(20, VectorUtils.DotProduct(new Vector2(2, 4), new Vector2(4, 3))); + Assert.AreEqual(0, VectorUtils.DotProduct(Vector3.Forward, Vector3.Up)); + Assert.AreEqual(8, VectorUtils.DotProduct(new Vector3(2, 3, 1), new Vector3(-1, 2, 4))); } [TestMethod] @@ -57,7 +57,7 @@ public void CanCalculateDistance() { var vector = new Vector3(10, 5, 3); var secondVector = new Vector3(5, 6, 1); // 5, -1, 2 // Magnitude: sqrt(30) - Assert.AreEqual(Math.Sqrt(30), vector.DistanceTo(secondVector)); + Assert.AreEqual(Math.Sqrt(30), vector.Distance(secondVector)); } [TestMethod] @@ -65,7 +65,7 @@ public void CanCalculateCrossProduct() { var vector = new Vector3(1, -5, 2); var secondVector = new Vector3(2, 0, 3); - var resultVector = vector.CrossProduct(secondVector); + var resultVector = vector.VectorProduct(secondVector); Assert.AreEqual(-15, resultVector.X); Assert.AreEqual(1, resultVector.Y); @@ -76,7 +76,7 @@ public void CanCalculateCrossProduct() public void CanGetCorrectLaTeXString() { var vector = new Vector3(1, 6, 8); - Assert.AreEqual(@"\left( \begin{array}{c} 1 \\ 6 \\ 8 \end{array} \right)", vector.LaTeXString); + Assert.AreEqual(@"\left( \begin{array}{c} 1 \\ 6 \\ 8 \end{array} \right)", vector.ToLaTeXString()); } [TestMethod] @@ -135,7 +135,7 @@ public void CanCalculateAngle() var vector2 = Vector2.UnitY; Assert.AreEqual(Math.PI/2, vector1.Angle(vector2)); - Assert.IsTrue(vector1.IsOrthogonalTo(vector2)); + Assert.IsTrue(vector1.CheckForOrthogonality(vector2)); } [TestMethod] @@ -170,7 +170,7 @@ public void CompareAreaCalculations() var secondVector = new Vector3(1, -2, 3); stopWatch.Start(); - double crossProductArea = Vector3.CrossProduct(firstVector, secondVector).Magnitude; + double crossProductArea = Vector3.VectorProduct(firstVector, secondVector).Magnitude; stopWatch.Stop(); // This is faster, if the vectors are already 3-dimensional, because we have no arccos, sin etc. Debug.Print("Vector3 area calculation over the cross product takes " + stopWatch.ElapsedMilliseconds + @@ -190,7 +190,7 @@ public void CompareAreaCalculations() stopWatch.Restart(); double secondCrossProductArea = - Vector3.CrossProduct(thirdVector.Convert(), fourthVector.Convert()).Magnitude; + Vector3.VectorProduct(thirdVector.Convert(), fourthVector.Convert()).Magnitude; stopWatch.Stop(); Debug.Print("Vector2 area calculation over the cross product takes " + stopWatch.ElapsedMilliseconds + " milliseconds."); @@ -207,47 +207,47 @@ public void CompareAreaCalculations() [TestMethod] public void CanDetermineIfVectorsAreOrthogonal() { - Assert.IsTrue(Vector3.Forward.IsOrthogonalTo(Vector3.Up)); - Assert.IsFalse(Vector3.Forward.IsOrthogonalTo(Vector3.Back)); - Assert.IsFalse(Vector3.Zero.IsOrthogonalTo(Vector3.UnitX)); + Assert.IsTrue(Vector3.Forward.CheckForOrthogonality(Vector3.Up)); + Assert.IsFalse(Vector3.Forward.CheckForOrthogonality(Vector3.Back)); + Assert.IsFalse(Vector3.Zero.CheckForOrthogonality(Vector3.UnitX)); } [TestMethod] public void CanDetermineIfVectorsAreOrthonormal() { - Assert.IsTrue(Vector3.Forward.IsOrthonormalTo(Vector3.Up)); - Assert.IsTrue(Vector3.Back.IsOrthonormalTo(Vector3.Down)); - Assert.IsFalse(Vector3.Forward.IsOrthonormalTo(Vector3.Back)); - Assert.IsFalse(Vector3.Forward.IsOrthonormalTo(new Vector3(2, 3, 2))); + Assert.IsTrue(Vector3.Forward.CheckForOrthonormality(Vector3.Up)); + Assert.IsTrue(Vector3.Back.CheckForOrthonormality(Vector3.Down)); + Assert.IsFalse(Vector3.Forward.CheckForOrthonormality(Vector3.Back)); + Assert.IsFalse(Vector3.Forward.CheckForOrthonormality(new Vector3(2, 3, 2))); } [TestMethod] public void CanDetermineIfVectorsAreParallel() { - Assert.IsTrue(new Vector3(2, 3, 3).IsParallelTo(new Vector3(4, 6, 6))); - Assert.IsTrue(new Vector3(1, 2, 3).IsParallelTo(new Vector3(3, 6, 9))); - Assert.IsFalse(new Vector3(0, 1, 3).IsParallelTo(new Vector3(0, 3, 2))); + Assert.IsTrue(new Vector3(2, 3, 3).CheckForParallelism(new Vector3(4, 6, 6))); + Assert.IsTrue(new Vector3(1, 2, 3).CheckForParallelism(new Vector3(3, 6, 9))); + Assert.IsFalse(new Vector3(0, 1, 3).CheckForParallelism(new Vector3(0, 3, 2))); } [TestMethod] public void CanConvertVectorIntoMatrices() { - var firstMatrix = new Matrix(3, 1) + var firstMatrix = new Matrix3x1() { [0, 0] = 1, [1, 0] = 0, [2, 0] = 0 }; - var firstVectorMatrix = Vector3.Right.AsVerticalMatrix(); + var firstVectorMatrix = Vector3.Right.AsVerticalMatrix(); Assert.AreEqual(firstMatrix, firstVectorMatrix); - var secondMatrix = new Matrix(1, 3) + var secondMatrix = new Matrix1x3() { [0, 0] = 1, [0, 1] = 0, [0, 2] = 0 }; - var secondVectorMatrix = Vector3.Right.AsHorizontalMatrix(); + var secondVectorMatrix = Vector3.Right.AsHorizontalMatrix(); Assert.AreEqual(secondMatrix, secondVectorMatrix); } @@ -255,31 +255,28 @@ public void CanConvertVectorIntoMatrices() public void CanCompareVectors() { // Let's see, if the dimension check is working - var vector = new Vector(2); - var secondVector = new Vector(3); + var vector = new Vector3(); + var secondVector = new Vector2(); Assert.IsFalse(vector.Equals(secondVector)); - Assert.IsFalse(vector == secondVector); - Assert.IsTrue(vector != secondVector); // Let's see, if the coordinate comparison is working (in this case simply two zero vectors) - var thirdVector = new Vector(4); - var fourthVector = new Vector(4); + var thirdVector = new Vector4(); + var fourthVector = new Vector4(); Assert.IsTrue(thirdVector.Equals(fourthVector)); Assert.IsTrue(thirdVector == fourthVector); Assert.IsFalse(thirdVector != fourthVector); // Let's see, if the coordinate comparison is working when we have the same dimension but different coordinate values - var fifthVector = new Vector(3) + var fifthVector = new Vector3() { [0] = 1, [1] = 2, [2] = 1 }; - - Assert.AreEqual(fifthVector, new Vector3(1, 2, 1)); + Assert.AreEqual(new Vector3(1, 2, 1), fifthVector); - var sixthVector = new Vector(3) + var sixthVector = new Vector3() { [0] = 2, [1] = 2, diff --git a/SharpMath/Algorithm.cs b/SharpMath/Algorithms.cs similarity index 58% rename from SharpMath/Algorithm.cs rename to SharpMath/Algorithms.cs index d05a93b..584edfb 100644 --- a/SharpMath/Algorithm.cs +++ b/SharpMath/Algorithms.cs @@ -13,53 +13,6 @@ namespace SharpMath /// public static class Algorithms { - /// - /// Implements the Gauss-Jordan-algorithm. - /// - /// The left side . - /// The right side . - /// The resulting . - /// The linear equation system cannot be solved clearly. - public static Matrix GaussJordan(Matrix leftSide, Matrix rightSide) - { - for (uint x = 0; x < leftSide.ColumnCount; x++) - { - uint nextX = x; - while (Math.Abs(leftSide[x, x]) < FloatingNumber.Epsilon) - { - nextX++; - - if (nextX >= leftSide.ColumnCount) - throw new EquationNotSolvableException("The linear equation system cannot be solved clearly."); - - if (Math.Abs(leftSide[x, nextX]) < FloatingNumber.Epsilon) - continue; - - leftSide.InterchangeRows(nextX, x); - rightSide.InterchangeRows(nextX, x); - } - - for (uint y = 0; y < leftSide.RowCount; y++) - { - if (y != x && Math.Abs(leftSide[y, x]) >= FloatingNumber.Epsilon) - { - double factor = leftSide[y, x]/leftSide[x, x]; - leftSide.SubtractRows(y, x, factor); - rightSide.SubtractRows(y, x, factor); - } - } - } - - for (uint i = 0; i < leftSide.ColumnCount; i++) - { - double factor = 1/leftSide[i, i]; - leftSide.MultiplyRow(i, factor); - rightSide.MultiplyRow(i, factor); - } - - return rightSide; - } - /// /// Converts infix tokens to postfix tokens. /// diff --git a/SharpMath/Equations/LinearEquationSystem.cs b/SharpMath/Equations/LinearEquationSystem.cs index a9d0b07..a313895 100644 --- a/SharpMath/Equations/LinearEquationSystem.cs +++ b/SharpMath/Equations/LinearEquationSystem.cs @@ -63,19 +63,19 @@ public double[] Solve() if (coefficientsCount == 0 || coefficientsCount != count) throw new EquationNotSolvableException("This linear equation system cannot be solved."); - var leftSide = new Matrix(count, count); - var rightSide = new Matrix(count, 1); + var leftSide = new VariableMatrix(count, count); + var rightSide = new VariableMatrix(count, 1); for (uint i = 0; i < count; ++i) { var currentEquation = Equations[(int) i]; double[] coefficients = currentEquation.Coefficients; - for (uint c = 0; c < coefficients.Count(); ++c) + for (uint c = 0; c < coefficients.Length; ++c) leftSide[i, c] = coefficients[c]; rightSide[i, 0] = currentEquation.Result; } - - var resultMatrix = Algorithms.GaussJordan(leftSide, rightSide); + + var resultMatrix = MatrixUtils.GaussJordan(leftSide, rightSide); double[] result = new double[rightSide.RowCount]; for (uint i = 0; i < resultMatrix.RowCount; ++i) result[i] = resultMatrix[i, 0]; diff --git a/SharpMath/Geometry/IMatrix.cs b/SharpMath/Geometry/IMatrix.cs new file mode 100644 index 0000000..38bcd19 --- /dev/null +++ b/SharpMath/Geometry/IMatrix.cs @@ -0,0 +1,10 @@ +namespace SharpMath.Geometry +{ + public interface IMatrix + { + uint RowCount { get; } + uint ColumnCount { get; } + double this[uint row, uint column] { get; set; } + double this[uint index] { get; set; } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/ISquareMatrix.cs b/SharpMath/Geometry/ISquareMatrix.cs new file mode 100644 index 0000000..bdb4b7e --- /dev/null +++ b/SharpMath/Geometry/ISquareMatrix.cs @@ -0,0 +1,16 @@ +namespace SharpMath.Geometry +{ + public interface ISquareMatrix : IMatrix + { + uint Dimension { get; } + double Determinant { get; } + double Trace { get; } + bool IsIdentity { get; } + bool IsOrthogonal { get; } + bool IsSymmetric { get; } + bool IsSkewSymmetric { get; } + bool IsAntiSymmetric { get; } + bool IsDiagonal { get; } + bool IsTriangle { get; } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/IVector.cs b/SharpMath/Geometry/IVector.cs new file mode 100644 index 0000000..4f16a59 --- /dev/null +++ b/SharpMath/Geometry/IVector.cs @@ -0,0 +1,37 @@ +namespace SharpMath.Geometry +{ + public interface IVector + { + /// + /// Gets or sets the value of the coordinate at the specified index. + /// + /// The index. + /// The value of the coordinate at the specified index. + double this[uint index] { get; set; } + + /// + /// Gets the dimension of the . + /// + uint Dimension { get; } + + /// + /// Gets the length of the . + /// + double Magnitude { get; } + + /// + /// Gets the squared length of the . + /// + double SquareMagnitude { get; } + + // + /// Gets a value indicating whether the is normalized, or not. + /// + bool IsNormalized { get; } + + /// + /// Gets a value indicating whether the has all of its components set to zero, or not. + /// + bool IsZero { get; } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Line2D.cs b/SharpMath/Geometry/Line2D.cs index dfbccc5..f00fbc3 100644 --- a/SharpMath/Geometry/Line2D.cs +++ b/SharpMath/Geometry/Line2D.cs @@ -8,16 +8,8 @@ namespace SharpMath.Geometry /// /// Represents a line in a 2-dimensional room. /// - public class Line2D : IEquatable + public struct Line2D : IEquatable { - /// - /// Initializes a new instance of the class. - /// - public Line2D() - : this(0, 0) - { - } - /// /// Initializes a new instance of the class. /// @@ -36,9 +28,11 @@ public Line2D(double slope, double offset) /// The second that should be used to create the . public Line2D(Point2D a, Point2D b) { - var line = FromPoints(a, b); - Slope = line.Slope; - Offset = line.Offset; + var vectorA = a.PositionVector; + var vectorB = b.PositionVector; + + Slope = (vectorB.Y - vectorA.Y) / (vectorB.X - vectorA.X); + Offset = vectorA.Y - (Slope * vectorA.X); // Insert a point } /// @@ -59,12 +53,7 @@ public Line2D(Point2D a, Point2D b) /// public static Line2D FromPoints(Point2D a, Point2D b) { - var vectorA = a.PositionVector; - var vectorB = b.PositionVector; - - var slope = (vectorB.Y - vectorA.Y)/(vectorB.X - vectorA.X); - var offset = vectorA.Y - (slope*vectorA.X); // Insert a point - return new Line2D(slope, offset); + return new Line2D(a, b); } /// @@ -169,7 +158,7 @@ public Point2D GetPoint(double x) /// public override bool Equals(object obj) { - return obj == null ? this == null : obj is Line2D && ((Line2D)obj).Offset == Offset && ((Line2D)obj).Slope == Slope; + return ReferenceEquals(obj, null) ? ReferenceEquals(this, null) : obj is Line2D && ((Line2D)obj).Offset == Offset && ((Line2D)obj).Slope == Slope; } /// @@ -181,7 +170,7 @@ public override bool Equals(object obj) /// public bool Equals(Line2D other) { - return other == null ? this == null : other.Offset == Offset && other.Slope == Slope; + return ReferenceEquals(other, null) ? ReferenceEquals(this, null) : other.Offset == Offset && other.Slope == Slope; } /// @@ -203,7 +192,7 @@ public override int GetHashCode() /// public override string ToString() { - return string.Format("Line2D [Offset={0}; Slope={1}]", Offset, Slope); + return $"Line2D [Offset={Offset}; Slope={Slope}]"; } /// diff --git a/SharpMath/Geometry/Line3D.cs b/SharpMath/Geometry/Line3D.cs index f6ffdc1..d2fbdcf 100644 --- a/SharpMath/Geometry/Line3D.cs +++ b/SharpMath/Geometry/Line3D.cs @@ -8,10 +8,8 @@ namespace SharpMath.Geometry /// /// Represents a 3D-line. /// - public class Line3D : IEquatable + public struct Line3D : IEquatable { - private Vector3 _direction; - /// /// Initializes a new instance of the class. /// @@ -20,22 +18,15 @@ public class Line3D : IEquatable public Line3D(Point3D point, Vector3 direction) { Point = point; + if (direction == Vector3.Zero) + throw new InvalidOperationException("The direction vector of the line must not be the zero vector."); Direction = direction; } /// /// Gets or sets a that represents the direction of the . /// - public Vector3 Direction - { - get { return _direction; } - set - { - if (value == Vector3.Zero) - throw new InvalidOperationException("The direction vector of the line must not be the zero vector."); - _direction = value; - } - } + public Vector3 Direction { get; set; } /// /// Gets or sets a point on the that the line intersects with. @@ -92,7 +83,7 @@ public bool IsPointOnLine(Point3D point) /// public override bool Equals(object obj) { - return obj == null ? this == null : obj is Line3D && ((Line3D)obj).Direction == Direction && ((Line3D)obj).Point == Point; + return ReferenceEquals(obj, null) ? ReferenceEquals(this, null) : obj is Line3D && ((Line3D)obj).Direction == Direction && ((Line3D)obj).Point == Point; } /// @@ -104,7 +95,7 @@ public override bool Equals(object obj) /// public bool Equals(Line3D other) { - return other == null ? this == null : other.Direction == Direction && other.Point == Point; + return ReferenceEquals(other, null) ? ReferenceEquals(this, null) : other.Direction == Direction && other.Point == Point; } /// diff --git a/SharpMath/Geometry/Matrix.cs b/SharpMath/Geometry/Matrix.cs deleted file mode 100644 index add7b5a..0000000 --- a/SharpMath/Geometry/Matrix.cs +++ /dev/null @@ -1,628 +0,0 @@ -// Author: Dominic Beger (Trade/ProgTrade) 2016 - -using System; -using System.Globalization; -using System.Text; - -namespace SharpMath.Geometry -{ - /// - /// Represents a matrix. - /// - public class Matrix : IEquatable - { - private readonly double[,] _fields; - - /// - /// Initializes a new instance of the class. - /// - /// The row count of the . - /// The column count of the . - public Matrix(uint rowCount, uint columnCount) - { - _fields = new double[rowCount, columnCount]; - RowCount = rowCount; - ColumnCount = columnCount; - } - - /// - /// Gets or sets the field value at the specified row and column indices. - /// - /// The row index. - /// The column index. - /// The field value at the specified row and column indices. - public double this[uint row, uint column] - { - get { return _fields[row, column]; } - set { _fields[row, column] = value; } - } - - /// - /// Gets the row count of this . - /// - public uint RowCount { get; } - - /// - /// Gets the column count of this . - /// - public uint ColumnCount { get; } - - /// - /// Gets a value indicating whether this is symmetric, or not. - /// - public bool IsSymmetric => (this == Transpose); - - /// - /// Gets a value indicating whether this is skew symmetric, or not. - /// - public bool IsSkewSymmetric => (Negate == Transpose); - - /// - /// Gets a value indicating whether this is antisymmetric, or not. - /// - public bool IsAntiSymmetric => (this == Transpose.Negate); - - /// - /// Gets a value indicating whether this is a square matrix, or not. - /// - public bool IsSquare => (RowCount == ColumnCount); - - /// - /// Gets a value indicating whether this is a diagonal matrix, or not. - /// - public bool IsDiagonal - { - get - { - for (uint y = 0; y < RowCount; ++y) - for (uint x = 0; x < ColumnCount; ++x) - { - if ((y == x && FloatingNumber.AreApproximatelyEqual(this[y, x], 0)) || - (y != x && !FloatingNumber.AreApproximatelyEqual(this[y, x], 0))) - return false; - } - return true; - } - } - - /// - /// Gets a value indicating whether this is a triangle matrix, or not. - /// - public bool IsTriangle - { - get - { - for (uint y = 0; y < RowCount; ++y) - for (uint x = 0; x < ColumnCount; ++x) - { - if ((y == x && FloatingNumber.AreApproximatelyEqual(this[y, x], 0)) || - (y != x && !FloatingNumber.AreApproximatelyEqual(this[y, x], 0))) - return false; - } - return true; - } - } - - /// - /// Gets the transpose of this . - /// - public Matrix Transpose - { - get - { - var resultMatrix = new Matrix(ColumnCount, RowCount); - for (uint y = 0; y < RowCount; ++y) - for (uint x = 0; x < ColumnCount; ++x) - resultMatrix[y, x] = this[x, y]; - return resultMatrix; - } - } - - /// - /// Gets the negated of this . - /// - public Matrix Negate - { - get - { - var resultMatrix = new Matrix(RowCount, ColumnCount); - for (uint y = 0; y < RowCount; ++y) - for (uint x = 0; x < ColumnCount; ++x) - resultMatrix[y, x] = -resultMatrix[y, x]; - return resultMatrix; - } - } - - /// - /// Gets a with all of its components set to zero. - /// - public Matrix Zero => new Matrix(RowCount, ColumnCount); - - public bool Equals(Matrix other) - { - if (ReferenceEquals(null, other)) - return false; - - return this == other; - } - - /// - /// Clones this instance. - /// - /// The cloned instance. - public object Clone() - { - var cloneMatrix = new Matrix(RowCount, ColumnCount); - for (uint y = 0; y < RowCount; ++y) - { - for (uint x = 0; x < ColumnCount; ++x) - { - cloneMatrix[y, x] = this[y, x]; - } - } - return cloneMatrix; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - var stringBuilder = new StringBuilder("{"); - - for (uint y = 0; y < RowCount; y++) - { - stringBuilder.AppendLine(); - stringBuilder.Append("\t"); - - for (uint x = 0; x < ColumnCount; x++) - { - stringBuilder.Append(this[y, x].ToString(CultureInfo.InvariantCulture)); - - if (x != (ColumnCount - 1)) - stringBuilder.Append(", "); - } - } - - return stringBuilder.Append("\n}").ToString(); - } - - /// - /// Gets the column at the specified index. - /// - /// The column index. - /// The column at the specified index. - public Vector GetColumnVector(uint index) - { - var resultVector = new Vector(ColumnCount); - for (uint i = 0; i < ColumnCount; ++i) - resultVector[i] = this[0, i]; - return resultVector; - } - - /// - /// Gets the row at the specified index. - /// - /// The column index. - /// The row at the specified index. - public Vector GetRowVector(uint index) - { - var resultVector = new Vector(RowCount); - for (uint i = 0; i < RowCount; ++i) - resultVector[i] = this[i, 0]; - return resultVector; - } - - /// - /// Augments this with the specified attaching it to the right. - /// - /// The to augment this one with. - /// The augmented . - public Matrix AugmentHorizontally(Matrix other) - { - if (RowCount != other.RowCount) - throw new InvalidOperationException( - "Cannot the augment the first matrix as its row count doesn't match the row count of the second one."); - - var resultMatrix = new Matrix(RowCount, ColumnCount + other.ColumnCount); - for (uint y = 0; y < resultMatrix.RowCount; ++y) - { - for (uint fx = 0; fx < ColumnCount; ++fx) - resultMatrix[y, fx] = this[y, fx]; - - for (uint sx = 0; sx < other.ColumnCount; ++sx) - resultMatrix[y, ColumnCount + sx] = other[y, sx]; - } - - return resultMatrix; - } - - /// - /// Augments a with an other attaching it to the right. - /// - /// The first . - /// The second to augment the first one with. - /// The augmented . - public static Matrix AugmentHorizontally(Matrix firstMatrix, Matrix secondMatrix) - { - return firstMatrix.AugmentHorizontally(secondMatrix); - } - - /// - /// Augments this with the specified by attaching at to the bottom. - /// - /// The to augment this one with. - /// The augmented . - public Matrix AugmentVertically(Matrix other) - { - if (ColumnCount != other.ColumnCount) - throw new InvalidOperationException( - "Cannot the augment the first matrix as its column count doesn't match the column count of the second one."); - - var resultMatrix = new Matrix(RowCount + other.RowCount, ColumnCount); - for (uint x = 0; x < resultMatrix.ColumnCount; ++x) - { - for (uint fy = 0; fy < RowCount; ++fy) - resultMatrix[fy, x] = this[fy, x]; - - for (uint sy = 0; sy < other.RowCount; ++sy) - resultMatrix[RowCount + sy, x] = other[sy, x]; - } - - return resultMatrix; - } - - /// - /// Augments a with an other by attaching it at the bottom. - /// - /// The first . - /// The second to augment the first one with. - /// The augmented . - public static Matrix AugmentVertically(Matrix firstMatrix, Matrix secondMatrix) - { - return firstMatrix.AugmentVertically(secondMatrix); - } - - /// - /// Calculates a sub of this by removing the specified column and row. - /// - /// The row that should be removed. - /// The column that should be removed. - /// The calculated sub . - public Matrix GetSubMatrix(uint row, uint column) - { - var resultMatrix = new Matrix(RowCount - 1, ColumnCount - 1); - uint y = 0; - for (uint cy = 0; cy < RowCount; cy++) - { - if (cy != row) - { - uint x = 0; - for (uint cx = 0; cx < ColumnCount; ++cx) - if (cx != column) - { - resultMatrix[y, x] = this[cy, cx]; - x++; - } - y++; - } - } - return resultMatrix; - } - - /// - /// Determines whether two instances are equal. - /// - /// The first matrix. - /// The second matrix. - /// true if the instances are equal, otherwise false. - public static bool AreEqual(Matrix firstMatrix, Matrix secondMatrix) - { - if (firstMatrix.ColumnCount != secondMatrix.ColumnCount || firstMatrix.RowCount != secondMatrix.RowCount) - return false; - - for (uint y = 0; y < firstMatrix.RowCount; ++y) - { - for (uint x = 0; x < firstMatrix.ColumnCount; ++x) - { - if (!FloatingNumber.AreApproximatelyEqual(firstMatrix[y, x], secondMatrix[y, x])) - return false; - } - } - - return true; - } - - internal void MultiplyRow(uint rowIndex, double factor) - { - for (uint x = 0; x < ColumnCount; ++x) - this[rowIndex, x] *= factor; - } - - internal void InterchangeRows(uint firstRowIndex, uint secondRowIndex) - { - for (uint x = 0; x < ColumnCount; ++x) - { - var firstValue = this[firstRowIndex, x]; - this[firstRowIndex, x] = this[secondRowIndex, x]; - this[secondRowIndex, x] = firstValue; - } - } - - internal void SubtractRows(uint firstRowIndex, uint secondRowIndex, double factor) - { - for (uint x = 0; x < ColumnCount; x++) - this[firstRowIndex, x] -= this[secondRowIndex, x]*factor; - } - - /// - /// Adds two instances, if they are compatible to each other. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix Add(Matrix firstMatrix, Matrix secondMatrix) - { - if (firstMatrix.ColumnCount != secondMatrix.ColumnCount) - throw new InvalidOperationException( - "Cannot the add the matrices as their amount of columns or rows are not equal."); - - for (uint y = 0; y < firstMatrix.RowCount; ++y) - for (uint x = 0; x < firstMatrix.ColumnCount; ++x) - firstMatrix[y, x] += secondMatrix[y, x]; - return firstMatrix; - } - - /// - /// Adds two instances, if they are compatible to each other. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix Subtract(Matrix firstMatrix, Matrix secondMatrix) - { - if (firstMatrix.ColumnCount != secondMatrix.ColumnCount) - throw new InvalidOperationException( - "Cannot the add the matrices as their amount of columns or rows are not equal."); - - for (uint y = 0; y < firstMatrix.RowCount; ++y) - for (uint x = 0; x < firstMatrix.ColumnCount; ++x) - firstMatrix[y, x] -= secondMatrix[y, x]; - return firstMatrix; - } - - /// - /// Multiplies a with a scalar. - /// - /// The to include into the product. - /// The scalar factor that the should be multiplied with. - /// Returns the product. - public static Matrix Multiply(Matrix matrix, double scalar) - { - for (uint y = 0; y < matrix.RowCount; ++y) - for (uint x = 0; x < matrix.ColumnCount; ++x) - matrix[y, x] *= scalar; - return matrix; - } - - /// - /// Multiplies two instances, if they are compatible to each other. - /// - /// The first to include into the product. - /// The second to include into the product. - /// The product. - public static Matrix Multiply(Matrix firstMatrix, Matrix secondMatrix) - { - if (firstMatrix.ColumnCount != secondMatrix.RowCount) - throw new ArgumentException( - "Cannot multiply the specified matrices because the column count of the first one does not match the row count of the second one."); - - var matrixProduct = new Matrix(firstMatrix.RowCount, secondMatrix.ColumnCount); - for (uint y = 0; y < matrixProduct.RowCount; ++y) - { - for (uint x = 0; x < matrixProduct.ColumnCount; ++x) - { - // Iteration condition could be secondMatrix.RowCount, too, as they are equal (see above) - for (uint i = 0; i < firstMatrix.ColumnCount; ++i) - matrixProduct[y, x] += firstMatrix[y, i]*secondMatrix[i, x]; - } - } - - return matrixProduct; - } - - /// - /// Multiplies the specified with the reciprocal of the specified scalar. - /// - /// The . - /// The scalar. - /// The resulting . - public static Matrix Divide(Matrix matrix, double scalar) - { - return Multiply(matrix, (1/scalar)); - } - - /// - /// Implements the operator +. - /// - /// The first . - /// The second . - /// - /// The result of the operator. - /// - public static Matrix operator +(Matrix firstMatrix, Matrix secondMatrix) - { - return Add(firstMatrix, secondMatrix); - } - - /// - /// Implements the operator -. - /// - /// The first . - /// The second . - /// - /// The result of the operator. - /// - public static Matrix operator -(Matrix firstMatrix, Matrix secondMatrix) - { - return Subtract(firstMatrix, secondMatrix); - } - - /// - /// Implements the operator * to multiply a with the specified scalar. - /// - /// The scalar. - /// The . - /// - /// The result of the operator. - /// - public static Matrix operator *(double scalar, Matrix matrix) - { - return Multiply(matrix, scalar); - } - - /// - /// Implements the operator * to multiply a with the specified scalar. - /// - /// The . - /// The scalar. - /// - /// The result of the operator. - /// - public static Matrix operator *(Matrix matrix, double scalar) - { - return Multiply(matrix, scalar); - } - - /// - /// Implements the operator *. - /// - /// The first . - /// The second . - /// - /// The result of the operator. - /// - public static Matrix operator *(Matrix firstMatrix, Matrix secondMatrix) - { - return Multiply(firstMatrix, secondMatrix); - } - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - - return this == obj as Matrix; - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - unchecked - { - int hash = 17; - for (uint y = 0; y < RowCount; ++y) - { - for (uint x = 0; x < ColumnCount; ++x) - { - hash = hash*23 + this[y, x].GetHashCode(); - } - } - return hash; - } - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public T Clone() where T : Matrix, new() - { - var cloneMatrix = new T(); - for (uint y = 0; y < RowCount; ++y) - { - for (uint x = 0; x < ColumnCount; ++x) - { - cloneMatrix[y, x] = this[y, x]; - } - } - return cloneMatrix; - } - - /// - /// Implements the operator ==. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator ==(Matrix left, Matrix right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.ColumnCount != right.ColumnCount || left.RowCount != right.RowCount) - return false; - - for (uint y = 0; y < left.RowCount; ++y) - { - for (uint x = 0; x < left.ColumnCount; ++x) - { - if (!FloatingNumber.AreApproximatelyEqual(left[y, x], right[y, x])) - return false; - } - } - - return true; - } - - /// - /// Implements the operator !=. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator !=(Matrix left, Matrix right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.ColumnCount != right.ColumnCount || left.RowCount != right.RowCount) - return true; - - for (uint y = 0; y < left.RowCount; ++y) - { - for (uint x = 0; x < left.ColumnCount; ++x) - { - if (!FloatingNumber.AreApproximatelyEqual(left[y, x], right[y, x])) - return true; - } - } - - return false; - } - } -} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix1x1.cs b/SharpMath/Geometry/Matrix1x1.cs new file mode 100644 index 0000000..6f1d919 --- /dev/null +++ b/SharpMath/Geometry/Matrix1x1.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 1x1 matrix. + /// + public struct Matrix1x1 : IEnumerable, ISquareMatrix, IEquatable + { + /// + /// Initializes a struct. + /// + /// The value at row 1 and column 1. + public Matrix1x1(double m11) + { + M11 = m11; + } + + /// + /// Gets the zero . + /// + public static Matrix1x1 Zero => new Matrix1x1(); + + /// + /// Gets the identity . + /// + public static Matrix1x1 Identity => MatrixUtils.GetIdentity(); + + /// + /// Gets or sets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: row + column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + default: throw new IndexOutOfRangeException("The index must be 0."); + } + } + + set + { + switch (index) + { + case 0: M11 = value; break; + default: throw new IndexOutOfRangeException("The index must be 0."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row + column]; + } + set + { + this[row + column] = value; + } + } + + /// + /// Gets the dimension of the . + /// + public uint Dimension => 1; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 1; + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 1; + + /// + /// Gets a value indicating whether the is singular, or not. If true, this + /// doesn't have an inverse. + /// + public bool IsSingular => Determinant.IsApproximatelyEqualTo(0); + + /// + /// Gets a value indicating whether the is orthogonal, or not. + /// + public bool IsOrthogonal => (this * Transpose) == Identity; + + /// + /// Gets a value indicating whether the is the identity , or not. + /// + public bool IsIdentity => M11.IsApproximatelyEqualTo(1); + + /// + /// Gets the inverse of the . + /// + public Matrix1x1 Inverse => MatrixUtils.GaussJordan(this, Identity); + + /// + /// Gets the determinant of the . + /// + public double Determinant => this.GetDeterminant(); // Determinant == M11 + + /// + /// Gets the trace of the . + /// + public double Trace => M11; + + /// + /// Gets the cofactor of the . + /// + public Matrix1x1 CofactorMatrix => this.BuildCofactorMatrix(); + + /// + /// Gets the adjugate of the . + /// + public Matrix1x1 Adjugate => CofactorMatrix.Transpose; + + /// + /// Gets a value indicating whether the is symmetric, or not. + /// + public bool IsSymmetric => this == Transpose; + + /// + /// Gets a value indicating whether the is skew symmetric, or not. + /// + public bool IsSkewSymmetric => Negate == Transpose; + + /// + /// Gets a value indicating whether the is antisymmetric, or not. + /// + public bool IsAntiSymmetric => this == Transpose.Negate; + + /// + /// Gets a value indicating whether the is a diagonal matrix, or not. + /// + public bool IsDiagonal => false; + + /// + /// Gets a value indicating whether the is a triangle matrix, or not. + /// + public bool IsTriangle => false; + + /// + /// Gets the negated of the . + /// + public Matrix1x1 Negate => this.GetNegate(); + + /// + /// Gets the transpose of the . + /// + public Matrix1x1 Transpose => this.GetTranspose(); + + /// + /// Implements the operator +. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix1x1 operator +(Matrix1x1 firstMatrix, Matrix1x1 secondMatrix) + { + return MatrixUtils.Add(firstMatrix, secondMatrix); + } + + /// + /// Implements the operator -. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix1x1 operator -(Matrix1x1 firstMatrix, Matrix1x1 secondMatrix) + { + return MatrixUtils.Subtract(firstMatrix, secondMatrix); + } + + /// + /// Implements the operator * to multiply a with the specified scalar. + /// + /// The scalar. + /// The . + /// + /// The result of the operator. + /// + public static Matrix1x1 operator *(double scalar, Matrix1x1 matrix) + { + return MatrixUtils.Multiply(matrix, scalar); + } + + /// + /// Implements the operator * to multiply a with the specified scalar. + /// + /// The . + /// The scalar. + /// + /// The result of the operator. + /// + public static Matrix1x1 operator *(Matrix1x1 matrix, double scalar) + { + return MatrixUtils.Multiply(matrix, scalar); + } + + /// + /// Implements the operator *. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix1x1 operator *(Matrix1x1 firstMatrix, Matrix1x1 secondMatrix) + { + return MatrixUtils.Multiply(firstMatrix, secondMatrix); + } + + /// + /// Determines whether the specified , is equal to the instance. + /// + /// The to compare with the instance. + /// + /// true if the specified is equal to the instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is Matrix1x1)) + return false; + return this == (Matrix1x1)obj; + } + + /// + /// Returns a hash code for the instance. + /// + /// + /// A hash code for the instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + return 17 * 23 + this[0, 0].GetHashCode(); + } + } + + public bool Equals(Matrix1x1 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + + /// + /// Implements the operator ==. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator ==(Matrix1x1 left, Matrix1x1 right) + { + return FloatingNumber.AreApproximatelyEqual(left[0, 0], right[0, 0]); + } + + /// + /// Implements the operator !=. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator !=(Matrix1x1 left, Matrix1x1 right) + { + return !FloatingNumber.AreApproximatelyEqual(left[0, 0], right[0, 0]); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix1x3.cs b/SharpMath/Geometry/Matrix1x3.cs new file mode 100644 index 0000000..ee4a176 --- /dev/null +++ b/SharpMath/Geometry/Matrix1x3.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 1x3 matrix for representing three-dimensional vectors horizontally. + /// + public struct Matrix1x3 : IEnumerable, IMatrix + { + /// + /// Gets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets the value at row 1 and column 2. + /// + public double M12 { get; set; } + + /// + /// Gets the value at row 1 and column 3. + /// + public double M13 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: (row +) column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M13; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M13 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row + column]; + } + + set + { + this[row + column] = value; + } + } + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 3; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 1; + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix1x4.cs b/SharpMath/Geometry/Matrix1x4.cs new file mode 100644 index 0000000..8e6c319 --- /dev/null +++ b/SharpMath/Geometry/Matrix1x4.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 1x4 matrix for representing four-dimensional vectors horizontally. + /// + public struct Matrix1x4 : IEnumerable, IMatrix + { + /// + /// Gets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets the value at row 1 and column 2. + /// + public double M12 { get; set; } + + /// + /// Gets the value at row 1 and column 3. + /// + public double M13 { get; set; } + + /// + /// Gets the value at row 1 and column 4. + /// + public double M14 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: (row +) column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M13; + case 3: return M14; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M13 = value; break; + case 3: M14 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row + column]; + } + + set + { + this[row + column] = value; + } + } + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 4; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 1; + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix2x2.cs b/SharpMath/Geometry/Matrix2x2.cs new file mode 100644 index 0000000..6b54768 --- /dev/null +++ b/SharpMath/Geometry/Matrix2x2.cs @@ -0,0 +1,341 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 2x2 matrix. + /// + public struct Matrix2x2 : IEnumerable, ISquareMatrix + { + /// + /// Initializes a struct. + /// + /// The value at row 1 and column 1. + /// The value at row 1 and column 2. + /// The value at row 2 and column 1. + /// The value at row 2 and column 2. + public Matrix2x2(double m11, double m12, double m21, double m22) + { + M11 = m11; + M12 = m12; + M21 = m21; + M22 = m22; + } + + /// + /// Gets the zero . + /// + public static Matrix2x2 Zero => new Matrix2x2(); + + /// + /// Gets the identity . + /// + public static Matrix2x2 Identity => MatrixUtils.GetIdentity(); + + /// + /// Gets or sets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 2. + /// + public double M12 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 1. + /// + public double M21 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 2. + /// + public double M22 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: (row*2) + column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M21; + case 3: return M22; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M21 = value; break; + case 3: M22 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row * 2 + column]; + } + set + { + this[row * 2 + column] = value; + } + } + + /// + /// Gets the dimension of the . + /// + public uint Dimension => 2; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 2; + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 2; + + /// + /// Gets a value indicating whether the is singular, or not. If true, this + /// doesn't have an inverse. + /// + public bool IsSingular => Determinant.IsApproximatelyEqualTo(0); + + /// + /// Gets a value indicating whether the is orthogonal, or not. + /// + public bool IsOrthogonal => (this * Transpose) == Identity; + + /// + /// Gets a value indicating whether the is the identity , or not. + /// + public bool IsIdentity => M11.IsApproximatelyEqualTo(1) && M22.IsApproximatelyEqualTo(0); + + /// + /// Gets the inverse of the . + /// + public Matrix2x2 Inverse => MatrixUtils.GaussJordan(this, Identity); + + /// + /// Gets the determinant of the . + /// + public double Determinant => this.GetDeterminant(); + + /// + /// Gets the trace of the . + /// + public double Trace => M11 + M22; + + /// + /// Gets the cofactor of the . + /// + public Matrix2x2 CofactorMatrix => this.BuildCofactorMatrix(); + + /// + /// Gets the adjugate of the . + /// + public Matrix2x2 Adjugate => CofactorMatrix.Transpose; + + /// + /// Gets a value indicating whether the is symmetric, or not. + /// + public bool IsSymmetric => this == Transpose; + + /// + /// Gets a value indicating whether the is skew symmetric, or not. + /// + public bool IsSkewSymmetric => Negate == Transpose; + + /// + /// Gets a value indicating whether the is antisymmetric, or not. + /// + public bool IsAntiSymmetric => this == Transpose.Negate; + + /// + /// Gets a value indicating whether the is a diagonal matrix, or not. + /// + public bool IsDiagonal => this.GetIsDiagonal(); + + /// + /// Gets a value indicating whether the is a triangle matrix, or not. + /// + public bool IsTriangle => this.GetIsTriangle(); + + /// + /// Gets the negated of the . + /// + public Matrix2x2 Negate => this.GetNegate(); + + /// + /// Gets the transpose of the . + /// + public Matrix2x2 Transpose => this.GetTranspose(); + + /// + /// Implements the operator +. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix2x2 operator +(Matrix2x2 firstMatrix, Matrix2x2 secondMatrix) + { + return MatrixUtils.Add(firstMatrix, secondMatrix); + } + + /// + /// Implements the operator -. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix2x2 operator -(Matrix2x2 firstMatrix, Matrix2x2 secondMatrix) + { + return MatrixUtils.Subtract(firstMatrix, secondMatrix); + } + + /// + /// Implements the operator * to multiply a with the specified scalar. + /// + /// The scalar. + /// The . + /// + /// The result of the operator. + /// + public static Matrix2x2 operator *(double scalar, Matrix2x2 matrix) + { + return MatrixUtils.Multiply(matrix, scalar); + } + + /// + /// Implements the operator * to multiply a with the specified scalar. + /// + /// The . + /// The scalar. + /// + /// The result of the operator. + /// + public static Matrix2x2 operator *(Matrix2x2 matrix, double scalar) + { + return MatrixUtils.Multiply(matrix, scalar); + } + + /// + /// Implements the operator *. + /// + /// The first . + /// The second . + /// + /// The result of the operator. + /// + public static Matrix2x2 operator *(Matrix2x2 firstMatrix, Matrix2x2 secondMatrix) + { + return MatrixUtils.Multiply(firstMatrix, secondMatrix); + } + + /// + /// Determines whether the specified , is equal to the instance. + /// + /// The to compare with the instance. + /// + /// true if the specified is equal to the instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is Matrix2x2)) + return false; + return this == (Matrix2x2)obj; + } + + /// + /// Returns a hash code for the instance. + /// + /// + /// A hash code for the instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + int hash = 17; + for (uint y = 0; y < RowCount; ++y) + { + for (uint x = 0; x < ColumnCount; ++x) + { + hash = hash * 23 + this[y, x].GetHashCode(); + } + } + return hash; + } + } + + public bool Equals(Matrix2x2 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + + /// + /// Implements the operator ==. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator ==(Matrix2x2 left, Matrix2x2 right) + { + return left.SequenceEqual(right); + } + + /// + /// Implements the operator !=. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator !=(Matrix2x2 left, Matrix2x2 right) + { + return !left.SequenceEqual(right); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix3x1.cs b/SharpMath/Geometry/Matrix3x1.cs new file mode 100644 index 0000000..672ab58 --- /dev/null +++ b/SharpMath/Geometry/Matrix3x1.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 3x1 matrix for representing three-dimensional vectors vertically. + /// + public struct Matrix3x1 : IEnumerable,IMatrix + { + /// + /// Gets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets the value at row 2 and column 1. + /// + public double M21 { get; set; } + + /// + /// Gets the value at row 3 and column 1. + /// + public double M31 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: row (+ column). + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M21; + case 2: return M31; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M21 = value; break; + case 2: M31 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row + column]; + } + + set + { + this[row + column] = value; + } + } + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 1; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 3; + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix3x3.cs b/SharpMath/Geometry/Matrix3x3.cs index 27c8dde..e38f833 100644 --- a/SharpMath/Geometry/Matrix3x3.cs +++ b/SharpMath/Geometry/Matrix3x3.cs @@ -1,6 +1,9 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { @@ -8,51 +11,261 @@ namespace SharpMath.Geometry /// Represents a 3x3 matrix and provides functions to transform 2-dimensional vectors. /// // ReSharper disable once InconsistentNaming - public class Matrix3x3 : SquareMatrix + [Serializable] + public struct Matrix3x3 : IEnumerable, IEquatable, ISquareMatrix { /// - /// wa - /// Creates a new instance of the class. + /// Initializes a struct. /// - public Matrix3x3() : base(3) + /// The value at row 1 and column 1. + /// The value at row 1 and column 2. + /// The value at row 1 and column 3. + /// The value at row 2 and column 1. + /// The value at row 2 and column 2 + /// The value at row 2 and column 3. + /// The value at row 3 and column 1. + /// The value at row 3 and column 2. + /// The value at row 3 and column 3. + public Matrix3x3(double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33) { + M11 = m11; + M12 = m12; + M13 = m13; + M21 = m21; + M22 = m22; + M23 = m23; + M31 = m31; + M32 = m32; + M33 = m33; } /// - /// Gets the identity . + /// Initializes a struct. /// - public static Matrix3x3 Identity => FromMatrix(GetIdentity(3)); + /// The first row . + /// The second row . + /// The third row . + public Matrix3x3(Vector3 row1, Vector3 row2, Vector3 row3) : this(row1.X, row2.X, row3.X, row1.Y, row2.Y, row3.Y, row1.Z, row2.Z, row3.Z) + { } /// - /// Creates a from an abstract object. + /// Gets the zero . /// - /// The to convert. - /// The that has been created. - public new static Matrix3x3 FromMatrix(Matrix matrix) + public static Matrix3x3 Zero => new Matrix3x3(); + + /// + /// Gets the identity . + /// + public static Matrix3x3 Identity => MatrixUtils.GetIdentity(); + + /// + /// Gets or sets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 2. + /// + public double M12 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 3. + /// + public double M13 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 1. + /// + public double M21 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 2. + /// + public double M22 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 3. + /// + public double M23 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 1. + /// + public double M31 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 2. + /// + public double M32 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 3. + /// + public double M33 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: (row*3) + column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] { - if (matrix.ColumnCount != 3 || matrix.RowCount != 3) - throw new ArgumentException("The matrix cannot be converted into a Matrix3x3"); + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M13; + case 3: return M21; + case 4: return M22; + case 5: return M23; + case 6: return M31; + case 7: return M32; + case 8: return M33; + default: throw new IndexOutOfRangeException("The index must be between 0 and 8."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M13 = value; break; + case 3: M21 = value; break; + case 4: M22 = value; break; + case 5: M23 = value; break; + case 6: M31 = value; break; + case 7: M32 = value; break; + case 8: M33 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 8."); + } + } + } - var resultMatrix = new Matrix3x3(); - for (uint y = 0; y < 3; ++y) - for (uint x = 0; x < 3; ++x) - resultMatrix[y, x] = matrix[y, x]; - return resultMatrix; + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row * 3 + column]; + } + set + { + this[row * 3 + column] = value; + } } /// - /// Creates a from a object. + /// Gets the dimension of the . + /// + public uint Dimension => 3; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 3; + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 3; + + /// + /// Gets a value indicating whether the is singular, or not. If true, this + /// doesn't have an inverse. + /// + public bool IsSingular => Determinant.IsApproximatelyEqualTo(0); + + /// + /// Gets a value indicating whether the is orthogonal, or not. + /// + public bool IsOrthogonal => (this * Transpose) == Identity; + + /// + /// Gets a value indicating whether the is the identity , or not. + /// + public bool IsIdentity => M11.IsApproximatelyEqualTo(1) && M22.IsApproximatelyEqualTo(1) && M33.IsApproximatelyEqualTo(1); + + /// + /// Gets the inverse of the . + /// + public Matrix3x3 Inverse => MatrixUtils.GaussJordan(this, Identity); + + /// + /// Gets the determinant of the . + /// + public double Determinant => this.GetDeterminant(); + + /// + /// Gets the trace of the . + /// + public double Trace => M11 + M22 + M33; + + /// + /// Gets the cofactor of the . + /// + public Matrix3x3 CofactorMatrix => this.BuildCofactorMatrix(); + + /// + /// Gets the adjugate of the . + /// + public Matrix3x3 Adjugate => CofactorMatrix.GetTranspose(); + + /// + /// Gets a value indicating whether the is symmetric, or not. + /// + public bool IsSymmetric => this == Transpose; + + /// + /// Gets a value indicating whether the is skew symmetric, or not. + /// + public bool IsSkewSymmetric => Negate == Transpose; + + /// + /// Gets a value indicating whether the is antisymmetric, or not. + /// + public bool IsAntiSymmetric => this == Transpose.Negate; + + /// + /// Gets a value indicating whether the is a diagonal matrix, or not. + /// + public bool IsDiagonal => this.GetIsDiagonal(); + + /// + /// Gets a value indicating whether the is a triangle matrix, or not. + /// + public bool IsTriangle => this.GetIsTriangle(); + + /// + /// Gets the negated of the . /// - /// The to convert. + public Matrix3x3 Negate => this.GetNegate(); + + /// + /// Gets the transpose of the . + /// + public Matrix3x3 Transpose => this.GetTranspose(); + + /// + /// Creates a from a object. + /// + /// The to convert. /// The that has been created. - public static Matrix3x3 FromMatrix(SquareMatrix matrix) + public static Matrix3x3 FromMatrix(IMatrix matrix) { - if (matrix.Dimension != 3) - throw new ArgumentException("The square matrix cannot be converted into a Matrix4x4"); + if (matrix.RowCount != 3 || matrix.ColumnCount != 3) + throw new ArgumentException("The square matrix cannot be converted into a Matrix3x3"); var resultMatrix = new Matrix3x3(); - for (uint y = 0; y < 3; ++y) - for (uint x = 0; x < 3; ++x) + for (uint y = 0; y < resultMatrix.Dimension; ++y) + for (uint x = 0; x < resultMatrix.Dimension; ++x) resultMatrix[y, x] = matrix[y, x]; return resultMatrix; } @@ -64,7 +277,7 @@ public static Matrix3x3 FromMatrix(SquareMatrix matrix) /// A that represents a rotation using the specified angle. public static Matrix3x3 Rotation(double angle) { - // This is actually the same as Matrix4x4.RotationZ as we are rotating around the Z-axis that is 0 in 2D-space. + // the is actually the same as Matrix4x4.RotationZ as we are rotating around the Z-axis that is 0 in 2D-space. double sin = Math.Sin(angle); double cos = Math.Cos(angle); @@ -118,7 +331,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Matrix3x3 operator +(Matrix3x3 firstMatrix, Matrix3x3 secondMatrix) { - return FromMatrix(Add(firstMatrix, secondMatrix)); + return MatrixUtils.Add(firstMatrix, secondMatrix); } /// @@ -131,7 +344,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Matrix3x3 operator -(Matrix3x3 firstMatrix, Matrix3x3 secondMatrix) { - return FromMatrix(Subtract(firstMatrix, secondMatrix)); + return MatrixUtils.Subtract(firstMatrix, secondMatrix); } /// @@ -144,7 +357,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Matrix3x3 operator *(double scalar, Matrix3x3 matrix) { - return FromMatrix(Multiply(matrix, scalar)); + return MatrixUtils.Multiply(matrix, scalar); } /// @@ -157,7 +370,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Matrix3x3 operator *(Matrix3x3 matrix, double scalar) { - return FromMatrix(Multiply(matrix, scalar)); + return MatrixUtils.Multiply(matrix, scalar); } /// @@ -170,8 +383,8 @@ public static Matrix3x3 Translation(double x, double y) /// public static Vector2 operator *(Matrix3x3 matrix, Vector2 vector) { - var resultMatrix = Multiply(matrix, new Vector3(vector.X, vector.Y, 1).AsVerticalMatrix()); - return resultMatrix.GetRowVector(0).Convert(); + var resultMatrix = MatrixUtils.Multiply(matrix, new Vector3(vector.X, vector.Y, 1).AsVerticalMatrix()); + return new Vector2(resultMatrix[0], resultMatrix[1]); } /// @@ -184,8 +397,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Vector2 operator *(Vector2 vector, Matrix3x3 matrix) { - var resultMatrix = Multiply(matrix, new Vector3(vector.X, vector.Y, 1).AsVerticalMatrix()); - return resultMatrix.GetRowVector(0).Convert(); + return matrix * vector; } /// @@ -198,8 +410,8 @@ public static Matrix3x3 Translation(double x, double y) /// public static Vector3 operator *(Matrix3x3 matrix, Vector3 vector) { - var resultMatrix = Multiply(matrix, vector.AsVerticalMatrix()); - return Vector3.FromVector(resultMatrix.GetRowVector(0)); + var resultMatrix = MatrixUtils.Multiply(matrix, vector.AsVerticalMatrix()); + return new Vector3(resultMatrix[0], resultMatrix[1], resultMatrix[2]); } /// @@ -212,8 +424,7 @@ public static Matrix3x3 Translation(double x, double y) /// public static Vector3 operator *(Vector3 vector, Matrix3x3 matrix) { - var resultMatrix = Multiply(matrix, vector.AsVerticalMatrix()); - return Vector3.FromVector(resultMatrix.GetRowVector(0)); + return matrix * vector; } /// @@ -226,7 +437,84 @@ public static Matrix3x3 Translation(double x, double y) /// public static Matrix3x3 operator *(Matrix3x3 firstMatrix, Matrix3x3 secondMatrix) { - return FromMatrix(Multiply(firstMatrix, secondMatrix)); + return MatrixUtils.Multiply(firstMatrix, secondMatrix); + } + + /// + /// Determines whether the specified , is equal to the instance. + /// + /// The to compare with the instance. + /// + /// true if the specified is equal to the instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is Matrix3x3)) + return false; + return this == (Matrix3x3)obj; + } + + /// + /// Returns a hash code for the instance. + /// + /// + /// A hash code for the instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + int hash = 17; + for (uint y = 0; y < RowCount; ++y) + { + for (uint x = 0; x < ColumnCount; ++x) + { + hash = hash * 23 + this[y, x].GetHashCode(); + } + } + return hash; + } + } + + public bool Equals(Matrix3x3 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + + /// + /// Implements the operator ==. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator ==(Matrix3x3 left, Matrix3x3 right) + { + return left.SequenceEqual(right); + } + + /// + /// Implements the operator !=. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator !=(Matrix3x3 left, Matrix3x3 right) + { + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix4x1.cs b/SharpMath/Geometry/Matrix4x1.cs new file mode 100644 index 0000000..a0271b0 --- /dev/null +++ b/SharpMath/Geometry/Matrix4x1.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + /// + /// Represents a 4x1 matrix for representing four-dimensional vectors vertically. + /// + public struct Matrix4x1 : IEnumerable, IMatrix + { + /// + /// Gets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets the value at row 2 and column 1. + /// + public double M21 { get; set; } + + /// + /// Gets the value at row 3 and column 1. + /// + public double M31 { get; set; } + + /// + /// Gets the value at row 4 and column 1. + /// + public double M41 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: row (+ column). + /// + /// The index. + /// The value at the specified index. + public double this[uint index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M21; + case 2: return M31; + case 3: return M41; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M21 = value; break; + case 2: M31 = value; break; + case 3: M41 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + } + + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row + column]; + } + + set + { + this[row + column] = value; + } + } + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 1; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 4; + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Matrix4x4.cs b/SharpMath/Geometry/Matrix4x4.cs index 2c194ff..a0907a2 100644 --- a/SharpMath/Geometry/Matrix4x4.cs +++ b/SharpMath/Geometry/Matrix4x4.cs @@ -1,6 +1,9 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { @@ -8,50 +11,324 @@ namespace SharpMath.Geometry /// Represents a 4x4 matrix and provides functions to transform 3-dimensional vectors. /// // ReSharper disable once InconsistentNaming - public class Matrix4x4 : SquareMatrix + public struct Matrix4x4 : IEnumerable, IEquatable, ISquareMatrix { /// - /// Creates a new instance of the class. + /// Initializes a struct. /// - public Matrix4x4() : base(4) + /// The value at row 1 and column 1. + /// The value at row 1 and column 2. + /// The value at row 1 and column 3. + /// The value at row 1 and column 4. + /// The value at row 2 and column 1. + /// The value at row 2 and column 2 + /// The value at row 2 and column 3. + /// The value at row 2 and column 4. + /// The value at row 3 and column 1. + /// The value at row 3 and column 2. + /// The value at row 3 and column 3. + /// The value at row 3 and column 4. + /// The value at row 4 and column 1. + /// The value at row 4 and column 2. + /// The value at row 4 and column 3. + /// The value at row 4 and column 4. + public Matrix4x4(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44) { + M11 = m11; + M12 = m12; + M13 = m13; + M14 = m14; + M21 = m21; + M22 = m22; + M23 = m23; + M24 = m24; + M31 = m31; + M32 = m32; + M33 = m33; + M34 = m34; + M41 = m41; + M42 = m42; + M43 = m43; + M44 = m44; } /// - /// Gets the identity . + /// Initializes a struct. /// - public static Matrix4x4 Identity => FromMatrix(GetIdentity(4)); + /// The first row . + /// The second row . + /// The third row . + /// The fourth row . + public Matrix4x4(Vector4 row1, Vector4 row2, Vector4 row3, Vector4 row4) : this(row1.X, row2.X, row3.X, row4.X, row1.Y, row2.Y, row3.Y, row4.Y, row1.Z, row2.Z, row3.Z, row4.Z, row1.W, row2.W, row3.W, row4.W) + { } /// - /// Creates a from an abstract object. + /// Gets the zero . /// - /// The to convert. - /// The that has been created. - public new static Matrix4x4 FromMatrix(Matrix matrix) + public static Matrix4x4 Zero => new Matrix4x4(); + + /// + /// Gets or sets the identity . + /// + public static Matrix4x4 Identity => MatrixUtils.GetIdentity(); + + /// + /// Gets or sets the value at row 1 and column 1. + /// + public double M11 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 2. + /// + public double M12 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 3. + /// + public double M13 { get; set; } + + /// + /// Gets or sets the value at row 1 and column 4. + /// + public double M14 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 1. + /// + public double M21 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 2. + /// + public double M22 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 3. + /// + public double M23 { get; set; } + + /// + /// Gets or sets the value at row 2 and column 4. + /// + public double M24 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 1. + /// + public double M31 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 2. + /// + public double M32 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 3. + /// + public double M33 { get; set; } + + /// + /// Gets or sets the value at row 3 and column 4. + /// + public double M34 { get; set; } + + /// + /// Gets or sets the value at row 4 and column 1. + /// + public double M41 { get; set; } + + /// + /// Gets or sets the value at row 4 and column 2. + /// + public double M42 { get; set; } + + /// + /// Gets or sets the value at row 4 and column 3. + /// + public double M43 { get; set; } + + /// + /// Gets or sets the value at row 4 and column 4. + /// + public double M44 { get; set; } + + /// + /// Gets or sets the value at the specified index. The values are accessed as: (row*4) + column. + /// + /// The index. + /// The value at the specified index. + public double this[uint index] { - if (!matrix.IsSquare || matrix.RowCount != 4 || matrix.ColumnCount != 4) - throw new ArgumentException("The matrix cannot be converted into a Matrix4x4."); + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M13; + case 3: return M14; + case 4: return M21; + case 5: return M22; + case 6: return M23; + case 7: return M24; + case 8: return M31; + case 9: return M32; + case 10: return M33; + case 11: return M34; + case 12: return M41; + case 13: return M42; + case 14: return M43; + case 15: return M44; + default: throw new IndexOutOfRangeException("The index must be between 0 and 15."); + } + } + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M13 = value; break; + case 3: M14 = value; break; + case 4: M21 = value; break; + case 5: M22 = value; break; + case 6: M23 = value; break; + case 7: M24 = value; break; + case 8: M31 = value; break; + case 9: M32 = value; break; + case 10: M33 = value; break; + case 11: M34 = value; break; + case 12: M41 = value; break; + case 13: M42 = value; break; + case 14: M43 = value; break; + case 15: M44 = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 15."); + } + } + } - var resultMatrix = new Matrix4x4(); - for (uint y = 0; y < 4; ++y) - for (uint x = 0; x < 4; ++x) - resultMatrix[y, x] = matrix[y, x]; - return resultMatrix; + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row. + /// The column. + /// The value at the specified row and column. + public double this[uint row, uint column] + { + get + { + return this[row * 4 + column]; + } + set + { + this[row * 4 + column] = value; + } } /// - /// Creates a from a object. + /// Gets the dimension of the . + /// + public uint Dimension => 4; + + /// + /// Gets the row count of the . + /// + public uint RowCount => 4; + + /// + /// Gets the column count of the . + /// + public uint ColumnCount => 4; + + /// + /// Gets a value indicating whether the is singular, or not. If true, this + /// doesn't have an inverse. + /// + public bool IsSingular => Determinant.IsApproximatelyEqualTo(0); + + /// + /// Gets a value indicating whether the is orthogonal, or not. + /// + public bool IsOrthogonal => (this * Transpose) == Identity; + + /// + /// Gets a value indicating whether the is the identity , or not. + /// + public bool IsIdentity => M11.IsApproximatelyEqualTo(1) && M22.IsApproximatelyEqualTo(1) && M33.IsApproximatelyEqualTo(1); + + /// + /// Gets the inverse of the . + /// + public Matrix4x4 Inverse => MatrixUtils.GaussJordan(this, Identity); + + /// + /// Gets the determinant of the . + /// + public double Determinant => this.GetDeterminant(); + + /// + /// Gets the trace of the . + /// + public double Trace => M11 + M22 + M33 + M44; + + /// + /// Gets the cofactor of the . + /// + public Matrix4x4 CofactorMatrix => this.BuildCofactorMatrix(); + + /// + /// Gets the adjugate of the . + /// + public Matrix4x4 Adjugate => CofactorMatrix.GetTranspose(); + + /// + /// Gets a value indicating whether the is symmetric, or not. + /// + public bool IsSymmetric => this == Transpose; + + /// + /// Gets a value indicating whether the is skew symmetric, or not. + /// + public bool IsSkewSymmetric => Negate == Transpose; + + /// + /// Gets a value indicating whether the is antisymmetric, or not. + /// + public bool IsAntiSymmetric => this == Transpose.Negate; + + /// + /// Gets a value indicating whether the is a diagonal matrix, or not. /// - /// The to convert. + public bool IsDiagonal => this.GetIsDiagonal(); + + /// + /// Gets a value indicating whether the is a triangle matrix, or not. + /// + public bool IsTriangle => this.GetIsTriangle(); + + /// + /// Gets the negated of the . + /// + public Matrix4x4 Negate => this.GetNegate(); + + /// + /// Gets the transpose of the . + /// + public Matrix4x4 Transpose => this.GetTranspose(); + + /// + /// Creates a from an abstract object. + /// + /// The to convert. /// The that has been created. - public static Matrix4x4 FromMatrix(SquareMatrix matrix) + public static Matrix4x4 FromMatrix(IMatrix matrix) { - if (matrix.Dimension != 4) - throw new ArgumentException("The square matrix cannot be converted into a Matrix4x4."); + if (matrix.RowCount != 4 || matrix.ColumnCount != 4) + throw new ArgumentException("The square matrix cannot be converted into a Matrix4x4"); var resultMatrix = new Matrix4x4(); - for (uint y = 0; y < 4; ++y) - for (uint x = 0; x < 4; ++x) + for (uint y = 0; y < resultMatrix.Dimension; ++y) + for (uint x = 0; x < resultMatrix.Dimension; ++x) resultMatrix[y, x] = matrix[y, x]; return resultMatrix; } @@ -213,8 +490,8 @@ public static Matrix4x4 PerspectiveFieldOfView(ProjectionData projectionData) public static Matrix4x4 View(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 upVector) { Vector3 vector = Vector3.FromVector((cameraPosition - cameraTarget).Normalize()); - Vector3 vector2 = Vector3.FromVector(Vector3.CrossProduct(upVector, vector)); - Vector3 vector3 = Vector3.FromVector(Vector3.CrossProduct(vector, vector2)); + Vector3 vector2 = Vector3.FromVector(Vector3.VectorProduct(upVector, vector)); + Vector3 vector3 = Vector3.FromVector(Vector3.VectorProduct(vector, vector2)); var matrix = Identity; matrix[0, 0] = vector2.X; @@ -229,9 +506,9 @@ public static Matrix4x4 View(Vector3 cameraPosition, Vector3 cameraTarget, Vecto matrix[2, 1] = vector3.Z; matrix[2, 2] = vector.Z; - matrix[3, 0] = -Vector.ScalarProduct(vector2, cameraPosition); - matrix[3, 1] = -Vector.ScalarProduct(vector3, cameraPosition); - matrix[3, 2] = -Vector.ScalarProduct(vector, cameraPosition); + matrix[3, 0] = -VectorUtils.DotProduct(vector2, cameraPosition); + matrix[3, 1] = -VectorUtils.DotProduct(vector3, cameraPosition); + matrix[3, 2] = -VectorUtils.DotProduct(vector, cameraPosition); matrix[3, 3] = 1f; return matrix; @@ -240,14 +517,14 @@ public static Matrix4x4 View(Vector3 cameraPosition, Vector3 cameraTarget, Vecto /// /// Creates a world by using the specified components. /// - /// - /// - /// - /// + /// The position. + /// The target vector. + /// The up vector. + /// The created world . public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) { - var xVector = Vector3.CrossProduct(forward, up); - var yVector = Vector3.CrossProduct(xVector, forward); + var xVector = Vector3.VectorProduct(forward, up); + var yVector = Vector3.VectorProduct(xVector, forward); var zVector = Vector3.FromVector(forward.Normalize()); xVector.Normalize(); yVector.Normalize(); @@ -282,7 +559,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Matrix4x4 operator +(Matrix4x4 firstMatrix, Matrix4x4 secondMatrix) { - return FromMatrix(Add(firstMatrix, secondMatrix)); + return MatrixUtils.Add(firstMatrix, secondMatrix); } /// @@ -295,7 +572,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Matrix4x4 operator -(Matrix4x4 firstMatrix, Matrix4x4 secondMatrix) { - return FromMatrix(Subtract(firstMatrix, secondMatrix)); + return MatrixUtils.Subtract(firstMatrix, secondMatrix); } /// @@ -308,7 +585,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Matrix4x4 operator *(double scalar, Matrix4x4 matrix) { - return FromMatrix(Multiply(matrix, scalar)); + return MatrixUtils.Multiply(matrix, scalar); } /// @@ -321,7 +598,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Matrix4x4 operator *(Matrix4x4 matrix, double scalar) { - return FromMatrix(Multiply(matrix, scalar)); + return MatrixUtils.Multiply(matrix, scalar); } /// @@ -334,7 +611,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Matrix4x4 operator *(Matrix4x4 firstMatrix, Matrix4x4 secondMatrix) { - return FromMatrix(Multiply(firstMatrix, secondMatrix)); + return MatrixUtils.Multiply(firstMatrix, secondMatrix); } /// @@ -347,11 +624,11 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Vector3 operator *(Matrix4x4 matrix, Vector3 vector) { - var resultMatrix = Multiply(matrix, new Vector4(vector.X, vector.Y, vector.Z, 1).AsVerticalMatrix()); - var resultVector = Vector4.FromVector(resultMatrix.GetRowVector(0)); + var resultMatrix = MatrixUtils.Multiply(matrix, new Vector4(vector.X, vector.Y, vector.Z, 1).AsVerticalMatrix()); + var resultVector = new Vector4(resultMatrix[0], resultMatrix[1], resultMatrix[2], resultMatrix[3]); resultVector.X /= resultVector.W; + resultVector.Y /= resultVector.W; resultVector.Z /= resultVector.W; - resultVector.X /= resultVector.W; return resultVector.Convert(); } @@ -365,12 +642,7 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Vector3 operator *(Vector3 vector, Matrix4x4 matrix) { - var resultMatrix = Multiply(matrix, new Vector4(vector.X, vector.Y, vector.Z, 1).AsVerticalMatrix()); - var resultVector = Vector4.FromVector(resultMatrix.GetRowVector(0)); - resultVector.X /= resultVector.W; - resultVector.Z /= resultVector.W; - resultVector.X /= resultVector.W; - return resultVector.Convert(); + return matrix * vector; } /// @@ -383,8 +655,8 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Vector4 operator *(Matrix4x4 matrix, Vector4 vector) { - var resultMatrix = Multiply(matrix, vector.AsVerticalMatrix()); - return Vector4.FromVector(resultMatrix.GetRowVector(0)); + var resultMatrix = MatrixUtils.Multiply(matrix, vector.AsVerticalMatrix()); + return new Vector4(resultMatrix[0], resultMatrix[1], resultMatrix[2], resultMatrix[3]); } /// @@ -397,8 +669,84 @@ public static Matrix4x4 World(Vector3 position, Vector3 forward, Vector3 up) /// public static Vector4 operator *(Vector4 vector, Matrix4x4 matrix) { - var resultMatrix = Multiply(matrix, vector.AsVerticalMatrix()); - return Vector4.FromVector(resultMatrix.GetRowVector(0)); + return matrix * vector; + } + + /// + /// Determines whether the specified , is equal to the instance. + /// + /// The to compare with the instance. + /// + /// true if the specified is equal to the instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is Matrix4x4)) + return false; + return this == (Matrix4x4)obj; + } + + /// + /// Returns a hash code for the instance. + /// + /// + /// A hash code for the instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + int hash = 17; + for (uint y = 0; y < RowCount; ++y) + { + for (uint x = 0; x < ColumnCount; ++x) + { + hash = hash * 23 + this[y, x].GetHashCode(); + } + } + return hash; + } + } + + public bool Equals(Matrix4x4 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + return new MatrixEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new MatrixEnumerator(this); + } + + /// + /// Implements the operator ==. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator ==(Matrix4x4 left, Matrix4x4 right) + { + return left.SequenceEqual(right); + } + + /// + /// Implements the operator !=. + /// + /// The left . + /// The right . + /// + /// The result of the operator. + /// + public static bool operator !=(Matrix4x4 left, Matrix4x4 right) + { + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/MatrixEnumerator.cs b/SharpMath/Geometry/MatrixEnumerator.cs new file mode 100644 index 0000000..72b9836 --- /dev/null +++ b/SharpMath/Geometry/MatrixEnumerator.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +namespace SharpMath.Geometry +{ + internal class MatrixEnumerator : IEnumerator + { + IMatrix matrix; + int index; + + internal MatrixEnumerator(IMatrix m) + { + matrix = m; + index = -1; + } + + public double Current => matrix[(uint)index % matrix.ColumnCount, (uint)index / matrix.RowCount]; + + public void Dispose() { } + + object System.Collections.IEnumerator.Current + { + get + { + return Current; + } + } + + public bool MoveNext() + { + index++; + return index < matrix.ColumnCount + matrix.RowCount; + } + + public void Reset() + { + index = -1; + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/MatrixUtils.cs b/SharpMath/Geometry/MatrixUtils.cs new file mode 100644 index 0000000..73799bc --- /dev/null +++ b/SharpMath/Geometry/MatrixUtils.cs @@ -0,0 +1,363 @@ +using SharpMath.Equations.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SharpMath.Geometry +{ + public static class MatrixUtils + { + /// + /// Adds two instances. + /// + /// The first < see cref="IMatrix" />. + /// The second . + /// The resulting . + public static T Add(T firstMatrix, T secondMatrix) where T : IMatrix + { + for (uint y = 0; y < firstMatrix.RowCount; ++y) + for (uint x = 0; x < firstMatrix.ColumnCount; ++x) + firstMatrix[y, x] += secondMatrix[y, x]; + return firstMatrix; + } + + /// + /// Calculates the cofactor of the specified . + /// + /// The whose cofactor should be calculated. + /// The calculated cofactor . + public static T BuildCofactorMatrix(this T matrix) where T : ISquareMatrix, new() + { + var resultMatrix = new T(); + for (uint y = 0; y < matrix.RowCount; ++y) + { + for (uint x = 0; x < matrix.ColumnCount; ++x) + { + resultMatrix[y, x] = matrix.GetCofactor(y, x); + } + } + + return resultMatrix; + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// + /// A new object that is a copy of this instance. + /// + public static T Clone(this T matrix) where T : IMatrix, new() + { + var cloneMatrix = new T(); + for (uint y = 0; y < matrix.RowCount; ++y) + { + for (uint x = 0; x < matrix.ColumnCount; ++x) + { + cloneMatrix[y, x] = matrix[y, x]; + } + } + return cloneMatrix; + } + + /// + /// Implements the Gauss-Jordan-algorithm. + /// + /// The left side . + /// The right side . + /// The resulting . + /// The linear equation system cannot be solved clearly. + public static T GaussJordan(T leftSide, T rightSide) where T : IMatrix + { + for (uint x = 0; x < leftSide.ColumnCount; x++) + { + uint nextX = x; + while (leftSide[x, x].IsApproximatelyEqualTo(0)) + { + nextX++; + + if (nextX >= leftSide.ColumnCount) + throw new EquationNotSolvableException("The linear equation system cannot be solved clearly."); + + if (Math.Abs(leftSide[x, nextX]) < FloatingNumber.Epsilon) + continue; + + leftSide.InterchangeRows(nextX, x); + rightSide.InterchangeRows(nextX, x); + } + + for (uint y = 0; y < leftSide.RowCount; y++) + { + if (y != x && Math.Abs(leftSide[y, x]) >= FloatingNumber.Epsilon) + { + double factor = leftSide[y, x] / leftSide[x, x]; + leftSide.SubtractRows(y, x, factor); + rightSide.SubtractRows(y, x, factor); + } + } + } + + for (uint i = 0; i < leftSide.ColumnCount; i++) + { + double factor = 1 / leftSide[i, i]; + leftSide.MultiplyRow(i, factor); + rightSide.MultiplyRow(i, factor); + } + + return rightSide; + } + + /// + /// Calculates the cofactor of an element at the specified position in the . + /// + /// The . + /// The row of the element. + /// The column of the element. + /// The cofactor of the element in this . + public static double GetCofactor(this ISquareMatrix matrix, uint row, uint column) + { + return Math.Pow(-1, row + column) * matrix.GetSubMatrix(row, column).GetDeterminant(); + } + + /// + /// Calculates the determinant of the . + /// + /// The whose determinant should be calculated. + /// The determinant of the . + public static double GetDeterminant(this ISquareMatrix matrix) + { + switch (matrix.Dimension) + { + case 1: + return matrix[0, 0]; + case 2: + return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]; + default: + return LaplaceExpansion(matrix); + } + } + + /// + /// Calculates the identity of the specified type. + /// + /// The type whose identity should be calculated. + /// The identity . + public static T GetIdentity() where T : ISquareMatrix, new() + { + var resultMatrix = new T(); + for (uint i = 0; i < resultMatrix.Dimension; ++i) + resultMatrix[i, i] = 1; + return resultMatrix; + } + + /// + /// Determines whether the is a diagonal , or not. + /// + /// The . + /// true, if the is a diagonal , otherwise false. + public static bool GetIsDiagonal(this ISquareMatrix matrix) + { + if (matrix.Dimension == 1) + return false; + + for (uint y = 0; y < matrix.RowCount; ++y) + for (uint x = 0; x < matrix.ColumnCount; ++x) + { + if ((y == x && FloatingNumber.AreApproximatelyEqual(matrix[y, x], 0)) || + (y != x && !FloatingNumber.AreApproximatelyEqual(matrix[y, x], 0))) + return false; + } + return true; + } + + /// + /// Determines whether the is a triangle , or not. + /// + /// The . + /// true, if the is a triangle , otherwise false. + public static bool GetIsTriangle(this ISquareMatrix matrix) + { + if (matrix.Dimension == 1) + return false; + + bool isRightSide; + var upperTriangle = new List(); + var lowerTriangle = new List(); + for (uint y = 0; y < matrix.RowCount; ++y) + { + isRightSide = false; + for (uint x = 0; x < matrix.ColumnCount; ++x) + { + if (y == x) + { + isRightSide = true; + continue; + } + + if (isRightSide) + upperTriangle.Add(matrix[y, x]); + else + lowerTriangle.Add(matrix[y, x]); + } + } + + if (upperTriangle.All(val => val.IsApproximatelyEqualTo(0)) || lowerTriangle.All(val => val.IsApproximatelyEqualTo(0))) + return true; + return false; + } + + /// + /// Calculates the negated of the . + /// + /// The whose negated should be calculated. + /// The negated . + public static T GetNegate(this T matrix) where T : IMatrix, new() + { + var resultMatrix = new T(); + for (uint y = 0; y < matrix.RowCount; ++y) + for (uint x = 0; x < matrix.ColumnCount; ++x) + resultMatrix[y, x] = -matrix[y, x]; + return resultMatrix; + } + + /// + /// Calculates the sub of the current by removing the specified column and row. + /// + /// The row that should be removed. + /// The column that should be removed. + /// The calculated sub . + public static ISquareMatrix GetSubMatrix(this ISquareMatrix matrix, uint row, uint column) + { + ISquareMatrix resultMatrix = null; + switch (matrix.Dimension) + { + case 2: + resultMatrix = new Matrix1x1(); + break; + case 3: + resultMatrix = new Matrix2x2(); + break; + case 4: + resultMatrix = new Matrix3x3(); + break; + default: + throw new InvalidOperationException("Cannot build sub matrix of " + nameof(matrix) + " as there is no lower dimension defined for it."); + } + + uint y = 0; + for (uint cy = 0; cy < matrix.RowCount; cy++) + { + if (cy != row) + { + uint x = 0; + for (uint cx = 0; cx < matrix.ColumnCount; ++cx) + if (cx != column) + { + resultMatrix[y, x] = matrix[cy, cx]; + x++; + } + y++; + } + } + return resultMatrix; + } + + /// + /// Calculates the transpose of the . + /// + /// The whose transpose should be calculated. + /// The transpose . + public static T GetTranspose(this T matrix) where T : ISquareMatrix, new() + { + var resultMatrix = new T(); + for (uint y = 0; y < matrix.Dimension; ++y) + for (uint x = 0; x < matrix.ColumnCount; ++x) + resultMatrix[y, x] = matrix[x, y]; + return resultMatrix; + } + + internal static double LaplaceExpansion(this ISquareMatrix matrix) + { + double determinant = 0; + for (uint i = 0; i < matrix.Dimension; ++i) + determinant += matrix[i, 0] * matrix.GetCofactor(i, 0); // The sigma sign is equal to a for-loop with recursion. + return determinant; + } + + /// + /// Multiplies the with a scalar. + /// + /// The to include into the product. + /// The scalar factor that the should be multiplied with. + /// Returns the product. + public static T Multiply(this T matrix, double scalar) where T : IMatrix + { + for (uint y = 0; y < matrix.RowCount; ++y) + for (uint x = 0; x < matrix.ColumnCount; ++x) + matrix[y, x] *= scalar; + return matrix; + } + + /// + /// Multiplies two instances, if they are compatible to each other. + /// + /// The first to include into the product. + /// The second to include into the product. + /// The product. + public static TOut Multiply(IMatrix firstMatrix, IMatrix secondMatrix) where TOut : IMatrix, new() + { + if (firstMatrix.ColumnCount != secondMatrix.RowCount) + throw new ArgumentException("The column count of the first matrix does not match the row count of the second matrix."); + + var matrixProduct = new TOut(); + if (matrixProduct.RowCount != firstMatrix.RowCount || matrixProduct.ColumnCount != secondMatrix.ColumnCount) + throw new ArgumentException($"Type parameter TOut is not an adequate IMatrix-type as its constraints do not fit those of the resulting matrix. The constraints must be {firstMatrix.RowCount}x{secondMatrix.ColumnCount}."); + + for (uint y = 0; y < matrixProduct.RowCount; ++y) + { + for (uint x = 0; x < matrixProduct.ColumnCount; ++x) + { + for (uint i = 0; i < firstMatrix.ColumnCount; ++i) + matrixProduct[y, x] += firstMatrix[y, i] * secondMatrix[i, x]; + } + } + + return matrixProduct; + } + + internal static void MultiplyRow(this IMatrix matrix, uint rowIndex, double factor) + { + for (uint x = 0; x < matrix.ColumnCount; ++x) + matrix[rowIndex, x] *= factor; + } + + internal static void InterchangeRows(this IMatrix matrix, uint firstRowIndex, uint secondRowIndex) + { + for (uint x = 0; x < matrix.ColumnCount; ++x) + { + var firstValue = matrix[firstRowIndex, x]; + matrix[firstRowIndex, x] = matrix[secondRowIndex, x]; + matrix[secondRowIndex, x] = firstValue; + } + } + + /// + /// Adds two instances, if they are compatible to each other. + /// + /// The first . + /// The second . + /// The resulting . + public static T Subtract(T firstMatrix, T secondMatrix) where T : IMatrix + { + for (uint y = 0; y < firstMatrix.RowCount; ++y) + for (uint x = 0; x < firstMatrix.ColumnCount; ++x) + firstMatrix[y, x] -= secondMatrix[y, x]; + return firstMatrix; + } + + internal static void SubtractRows(this IMatrix matrix, uint firstRowIndex, uint secondRowIndex, double factor) + { + for (uint x = 0; x < matrix.ColumnCount; x++) + matrix[firstRowIndex, x] -= matrix[secondRowIndex, x] * factor; + } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Point.cs b/SharpMath/Geometry/Point.cs deleted file mode 100644 index e4ac147..0000000 --- a/SharpMath/Geometry/Point.cs +++ /dev/null @@ -1,321 +0,0 @@ -// Author: Dominic Beger (Trade/ProgTrade) 2016 -// Improvements: Stefan Baumann 2016 - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using SharpMath.Geometry.Exceptions; - -namespace SharpMath.Geometry -{ - /// - /// Represents a point. - /// - public class Point : IEnumerable, IEquatable - { - private readonly double[] _coordinateValues; - - /// - /// Initializes a new instance of the class. - /// - /// The dimension of the . - public Point(uint dimension) - { - Dimension = dimension; - _coordinateValues = new double[dimension]; - } - - /// - /// Initializes a new instance of the class. - /// - /// The coordinates of the . - public Point(params double[] coordinates) - { - Dimension = (uint) coordinates.Length; - _coordinateValues = coordinates; - } - - /// - /// Initializes a new instance of the class. - /// - /// The exisiting to copy. - public Point(Point point) - { - Dimension = point.Dimension; - _coordinateValues = new double[Dimension]; - for (uint i = 0; i < point.Dimension; ++i) - this[i] = point[i]; - } - - /// - /// Initializes a new instance of the class. - /// - /// The position of the to create. - public Point(Vector vector) - { - Dimension = vector.Dimension; - _coordinateValues = new double[Dimension]; - for (uint i = 0; i < vector.Dimension; ++i) - this[i] = vector[i]; - } - - /// - /// Gets the dimension of this . - /// - public uint Dimension { get; } - - /// - /// Gets or sets the value of the coordinate at the specified index. - /// - /// The index to use. - /// Returns the value of the coordinate. - public double this[uint index] - { - get { return _coordinateValues[index]; } - set { _coordinateValues[index] = value; } - } - - /// - /// Gets the position of this . - /// - public Vector PositionVector - { - get - { - var resultVector = new Vector(Dimension); - for (uint i = 0; i < Dimension; ++i) - resultVector[i] = this[i]; - return resultVector; - } - } - - /// - /// Returns an enumerator that iterates through the collection of coordinates. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - for (int i = 0; i < this.Dimension; i++) - { - yield return this[(uint)i]; - } - yield break; - } - - /// - /// Returns an enumerator that iterates through the collection of coordinates. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i < this.Dimension; i++) - { - yield return this[(uint)i]; - } - yield break; - } - - public bool Equals(Point other) - { - if (ReferenceEquals(null, other)) - return false; - - return this == other; - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public object Clone() - { - var clonePoint = new Point(Dimension); - for (uint i = 0; i < Dimension; ++i) - clonePoint[i] = this[i]; - return clonePoint; - } - - /// - /// Converts this into a of another dimension. - /// - /// The type that the current should be converted to. - /// This converted into the given type. - public T Convert() where T : Point, new() - // Type parameter because we need to create an instance of that specific type - { - var resultPoint = new T(); - if (resultPoint.Dimension == Dimension) - Debug.Print( - $"Point conversion method (Point{Dimension}.To()) is currently used to convert a point into one of the same dimension. Please check if this has been your intention."); - for (uint i = 0; i < Math.Min(Dimension, resultPoint.Dimension); ++i) - resultPoint[i] = this[i]; - return resultPoint; - } - - /// - /// Adds two instances. - /// - /// The first . - /// The second . - /// The resulting . - public static Point Add(Point first, Point second) - { - if (first.Dimension != second.Dimension) - throw new DimensionException("The dimensions of the points do not equal each other."); - - var resultPoint = new Point(first.Dimension); - for (uint i = 0; i < resultPoint.Dimension; ++i) - resultPoint[i] = first[i] + second[i]; - return resultPoint; - } - - /// - /// Subtracts two instances. - /// - /// The first . - /// The second . - /// The resulting . - public static Point Subtract(Point first, Point second) - { - if (first.Dimension != second.Dimension) - throw new DimensionException("The dimensions of the points do not equal each other."); - - var resultPoint = new Point(first.Dimension); - for (uint i = 0; i < resultPoint.Dimension; ++i) - resultPoint[i] = first[i] - second[i]; - return resultPoint; - } - - /// - /// Implements the operator +. - /// - /// The first . - /// The second . - /// - /// The result of the operator. - /// - public static Point operator +(Point first, Point second) - { - return Add(first, second); - } - - /// - /// Implements the operator -. - /// - /// The first . - /// The second . - /// - /// The result of the operator. - /// - public static Point operator -(Point first, Point second) - { - return Subtract(first, second); - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public T Clone() where T : Point, new() - { - var clonePoint = new T(); - for (uint i = 0; i < Dimension; ++i) - clonePoint[i] = this[i]; - return clonePoint; - } - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - - return this == obj as Point; - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - unchecked - { - int hash = 17; - for (uint i = 0; i < Dimension; ++i) - hash = hash*23 + this[i].GetHashCode(); - return hash; - } - } - - /// - /// Implements the operator ==. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator ==(Point left, Point right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.Dimension != right.Dimension) - return false; - - for (uint i = 0; i < left.Dimension; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; - } - - /// - /// Implements the operator !=. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator !=(Point left, Point right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.Dimension != right.Dimension) - return true; - - for (uint i = 0; i < left.Dimension; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/SharpMath/Geometry/Point2D.cs b/SharpMath/Geometry/Point2D.cs index 125373b..81d042b 100644 --- a/SharpMath/Geometry/Point2D.cs +++ b/SharpMath/Geometry/Point2D.cs @@ -1,81 +1,89 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { /// /// Represents a two-dimensional point. /// - public class Point2D : Point + public struct Point2D : IEquatable, IEnumerable { - /// - /// Initializes a new instance of the class. - /// - public Point2D() : base(2) - { - } - /// /// Initializes a new instance of the class. /// /// The X-coordinate. /// The Y-coordinate. - public Point2D(double x, double y) : base(x, y) + public Point2D(double x, double y) { + X = x; + Y = y; } /// /// Initializes a new instance of the class. /// /// The exisiting to copy. - public Point2D(Point2D point) : base(point) + public Point2D(Point2D point) { + X = point.X; + Y = point.Y; } /// /// Initializes a new instance of the class. /// /// The position of the to create. - public Point2D(Vector2 vector) : base(vector) + public Point2D(Vector2 vector) { + X = vector.X; + Y = vector.Y; } /// - /// Gets or sets the value of the X-coordinate. + /// Gets or sets the value of the coordinate at the specified index. /// - public double X + /// The index. + /// The value of the coordinate at the specified index. + public double this[uint index] { - get { return this[0]; } - set { this[0] = value; } + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + default: throw new IndexOutOfRangeException("The index must be between 0 and 1."); + } + } + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 1."); + } + } } /// - /// Gets or sets the value of the Y-coordinate. + /// Gets or sets the value of the X-coordinate. /// - public double Y - { - get { return this[1]; } - set { this[1] = value; } - } + public double X { get; set; } /// - /// Gets the position of this . + /// Gets or sets the value of the Y-coordinate. /// - public new Vector2 PositionVector => new Vector2(X, Y); + public double Y { get; set; } /// - /// Generates a from the base class, if the dimension is correct. + /// Gets the position of this . /// - /// The to generate a from. - /// The generated . - /// The dimension of the given is invalid. It must be 2. - public static Point2D FromPoint(Point point) - { - if (point.Dimension != 2) - throw new ArgumentException("The dimension of the given point is invalid. It must be 2."); - return new Point2D(point[0], point[1]); - } + public Vector2 PositionVector => new Vector2(X, Y); /// /// Implements the operator +. @@ -87,7 +95,10 @@ public static Point2D FromPoint(Point point) /// public static Point2D operator +(Point2D first, Point2D second) { - return FromPoint(Add(first, second)); + var resultPoint = new Point2D(); + for (uint i = 0; i < 2; ++i) + resultPoint[i] = first[i] + second[i]; + return resultPoint; } /// @@ -100,7 +111,10 @@ public static Point2D FromPoint(Point point) /// public static Point2D operator -(Point2D first, Point2D second) { - return FromPoint(Subtract(first, second)); + var resultPoint = new Point2D(); + for (uint i = 0; i < 2; ++i) + resultPoint[i] = first[i] - second[i]; + return resultPoint; } /// @@ -111,7 +125,7 @@ public static Point2D FromPoint(Point point) /// public override string ToString() { - return $"X: {this[0]}, Y: {this[1]}"; + return $"Point2D [X: {this[0]}, Y: {this[1]}]"; } /// @@ -123,18 +137,9 @@ public override string ToString() /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() == typeof (Point2D)) return this == (Point2D) obj; - var point = obj as Point; - if (Dimension != point?.Dimension) - return false; - return this == FromPoint(point); + return false; } /// @@ -154,6 +159,29 @@ public override int GetHashCode() } } + public bool Equals(Point2D other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + for (uint i = 0; i < 2; i++) + { + yield return this[i]; + } + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (uint i = 0; i < 2; i++) + { + yield return this[i]; + } + yield break; + } + /// /// Implements the operator ==. /// @@ -164,16 +192,7 @@ public override int GetHashCode() /// public static bool operator ==(Point2D left, Point2D right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 2; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return left.SequenceEqual(right); } /// @@ -186,16 +205,7 @@ public override int GetHashCode() /// public static bool operator !=(Point2D left, Point2D right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 2; ++i) - { - if (FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/Point3D.cs b/SharpMath/Geometry/Point3D.cs index 26f832e..b77c0f7 100644 --- a/SharpMath/Geometry/Point3D.cs +++ b/SharpMath/Geometry/Point3D.cs @@ -1,88 +1,97 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { - public class Point3D : Point + public struct Point3D : IEquatable, IEnumerable { - /// - /// Initializes a new instance of the class. - /// - public Point3D() : base(3) - { - } - /// /// Initializes a new instance of the class. /// /// The value of the X-coordinate. /// The value of the Y-coordinate. /// The value of the Z-coordinate. - public Point3D(double x, double y, double z) : base(x, y, z) + public Point3D(double x, double y, double z) { + X = x; + Y = y; + Z = z; } /// /// Initializes a new instance of the class. /// /// The exisiting to copy. - public Point3D(Point3D point) : base(point) + public Point3D(Point3D point) { + X = point.X; + Y = point.Y; + Z = point.Z; } /// /// Initializes a new instance of the class. /// /// The position of the to create. - public Point3D(Vector3 vector) : base(vector) + public Point3D(Vector3 vector) { + X = vector.X; + Y = vector.Y; + Z = vector.Z; } /// - /// Gets or sets the value of the X-coordinate. + /// Gets or sets the value of the coordinate at the specified index. /// - public double X + /// The index. + /// The value of the coordinate at the specified index. + public double this[uint index] { - get { return this[0]; } - set { this[0] = value; } + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } } /// - /// Gets or sets the value of the Y-coordinate. + /// Gets or sets the value of the X-coordinate. /// - public double Y - { - get { return this[1]; } - set { this[1] = value; } - } + public double X { get; set; } /// - /// Gets or sets the value of the Z-coordinate. + /// Gets or sets the value of the Y-coordinate. /// - public double Z - { - get { return this[2]; } - set { this[2] = value; } - } + public double Y { get; set; } /// - /// Gets the position of this . + /// Gets or sets the value of the Z-coordinate. /// - public new Vector3 PositionVector => new Vector3(X, Y, Z); + public double Z { get; set; } /// - /// Generates a from the base class, if the dimension is correct. + /// Gets the position of this . /// - /// The to generate a from. - /// The generated . - /// The dimension of the given is invalid. It must be 3. - public static Point3D FromPoint(Point point) - { - if (point.Dimension != 3) - throw new ArgumentException("The dimension of the given point is invalid. It must be 3."); - return new Point3D(point[0], point[1], point[2]); - } + public Vector3 PositionVector => new Vector3(X, Y, Z); /// /// Implements the operator +. @@ -94,7 +103,10 @@ public static Point3D FromPoint(Point point) /// public static Point3D operator +(Point3D first, Point3D second) { - return FromPoint(Add(first, second)); + var resultPoint = new Point3D(); + for (uint i = 0; i < 3; ++i) + resultPoint[i] = first[i] + second[i]; + return resultPoint; } /// @@ -107,7 +119,10 @@ public static Point3D FromPoint(Point point) /// public static Point3D operator -(Point3D first, Point3D second) { - return FromPoint(Subtract(first, second)); + var resultPoint = new Point3D(); + for (uint i = 0; i < 3; ++i) + resultPoint[i] = first[i] - second[i]; + return resultPoint; } /// @@ -118,7 +133,7 @@ public static Point3D FromPoint(Point point) /// public override string ToString() { - return $"X: {this[0]}, Y: {this[1]}, Z: {this[2]}"; + return $"Point3D [X: {this[0]}, Y: {this[1]}, Z: {this[2]}]"; } /// @@ -130,18 +145,9 @@ public override string ToString() /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() == typeof (Point3D)) return this == (Point3D) obj; - var point = obj as Point; - if (Dimension != point?.Dimension) - return false; - return this == FromPoint(point); + return false; } /// @@ -162,6 +168,29 @@ public override int GetHashCode() } } + public bool Equals(Point3D other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + for (uint i = 0; i < 3; i++) + { + yield return this[i]; + } + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (uint i = 0; i < 3; i++) + { + yield return this[i]; + } + yield break; + } + /// /// Implements the operator ==. /// @@ -172,16 +201,7 @@ public override int GetHashCode() /// public static bool operator ==(Point3D left, Point3D right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 3; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return left.SequenceEqual(right); } /// @@ -194,16 +214,7 @@ public override int GetHashCode() /// public static bool operator !=(Point3D left, Point3D right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 3; ++i) - { - if (FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/Polygon.cs b/SharpMath/Geometry/Polygon.cs index 199b6a9..49df65b 100644 --- a/SharpMath/Geometry/Polygon.cs +++ b/SharpMath/Geometry/Polygon.cs @@ -187,7 +187,7 @@ private int ContainsPointInternal(Point2D q, Point2D p1, Point2D p2) } /// - /// Returns an enumerator that iterates through the collection. + /// Returns an enumerator that iterates through the collection. /// /// /// An enumerator that can be used to iterate through the collection. @@ -217,7 +217,7 @@ IEnumerator IEnumerable.GetEnumerator() /// public override bool Equals(object obj) { - return obj == null ? this == null : obj is Polygon && ((Polygon)obj).Points.SequenceEqual(Points); + return ReferenceEquals(obj, null) ? ReferenceEquals(this, null) : obj is Polygon && ((Polygon)obj).Points.SequenceEqual(Points); } /// @@ -229,7 +229,7 @@ public override bool Equals(object obj) /// public bool Equals(Polygon other) { - return other == null ? this == null : other.Points.SequenceEqual(Points); + return ReferenceEquals(other, null) ? ReferenceEquals(this, null) : other.Points.SequenceEqual(Points); } /// @@ -244,14 +244,14 @@ public override int GetHashCode() } /// - /// Returns a that represents this instance. + /// Returns a that represents this instance. /// /// - /// A that represents this instance. + /// A that represents this instance. /// public override string ToString() { - return string.Format("Polygon {{{0}}}", string.Join(", ", Points)); + return $"Polygon {{{string.Join(", ", Points)}}}"; } /// diff --git a/SharpMath/Geometry/SquareMatrix.cs b/SharpMath/Geometry/SquareMatrix.cs deleted file mode 100644 index 548490c..0000000 --- a/SharpMath/Geometry/SquareMatrix.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Author: Dominic Beger (Trade/ProgTrade) 2016 - -using System; - -namespace SharpMath.Geometry -{ - /// - /// Represents a whose row and column count are equal. (n*n) - /// - public class SquareMatrix : Matrix - { - /// - /// Creates a new instance of the class. - /// - /// The dimension of the . - public SquareMatrix(uint dimension) - : base(dimension, dimension) - { - Dimension = dimension; - } - - /// - /// Gets the dimension (column and row count) of this . - /// - public uint Dimension { get; } - - /// - /// Gets a value indicating whether this is singular, or not. If true, this - /// doesn't have an inverse. - /// - public bool IsSingular => Math.Abs(Determinant) < FloatingNumber.Epsilon; - - /// - /// Gets a value indicating whether this is orthogonal, or not. - /// - public bool IsOrthogonal => Multiply(this, Transpose) == GetIdentity(Dimension); - - /// - /// Gets the inverse of this . - /// - public SquareMatrix Inverse => FromMatrix(Algorithms.GaussJordan(this, GetIdentity(ColumnCount))); - - /// - /// Gets the determinant of this . - /// - public double Determinant - { - get - { - switch (ColumnCount) - { - case 1: - return this[0, 0]; - case 2: - return this[0, 0]*this[1, 1] - this[0, 1]*this[1, 0]; - default: - return LaplaceExpansion(); - } - } - } - - /// - /// Gets the trace of this . - /// - public double Trace - { - get - { - double result = 0d; - for (uint i = 0; i < ColumnCount; ++i) - result += this[i, i]; - return result; - } - } - - /// - /// Gets the cofactor of this . - /// - public SquareMatrix CofactorMatrix => BuildCofactorMatrix(this); - - /// - /// Gets the adjugate of this . - /// - public SquareMatrix Adjugate => FromMatrix(CofactorMatrix.Transpose); - - /// - /// Creates a from an abstract object. - /// - /// The to convert. - /// The that has been created. - public static SquareMatrix FromMatrix(Matrix matrix) - { - if (matrix.ColumnCount != matrix.RowCount) - throw new InvalidOperationException( - "Cannot create a square matrix as the row count does not match the column count."); - - var squareMatrix = new SquareMatrix(matrix.ColumnCount); - for (uint y = 0; y < squareMatrix.Dimension; y++) - for (uint x = 0; x < squareMatrix.Dimension; x++) - squareMatrix[y, x] = matrix[y, x]; - - return squareMatrix; - } - - /// - /// Creates an identity with the specified dimension. - /// - public static SquareMatrix GetIdentity(uint dimension) - { - var resultMatrix = new SquareMatrix(dimension); - for (uint i = 0; i < resultMatrix.ColumnCount; ++i) - resultMatrix[i, i] = 1; - return resultMatrix; - } - - internal double LaplaceExpansion() - { - double determinant = 0; - for (uint y = 0; y < Dimension; ++y) - determinant += this[y, 0]*GetCofactor(y, 0); // The sigma sign is equal to a for-loop with recursion. - - return determinant; - } - - /// - /// Calculates a cofactor of an element at the specified position in this . - /// - /// The row of the element. - /// The column of the element. - /// The cofactor of the element in this . - public double GetCofactor(uint row, uint column) - { - return Math.Pow(-1, row + column)*FromMatrix(GetSubMatrix(row, column)).Determinant; - } - - /// - /// Calculates the cofactor of the specified . - /// - /// The whose cofactor should be calculated. - /// The calculated cofactor . - public static SquareMatrix BuildCofactorMatrix(SquareMatrix matrix) - { - var resultMatrix = new SquareMatrix(matrix.Dimension); - for (uint y = 0; y < matrix.RowCount; ++y) - { - for (uint x = 0; x < matrix.ColumnCount; ++x) - { - resultMatrix[y, x] = matrix.GetCofactor(y, x); - } - } - - return resultMatrix; - } - } -} \ No newline at end of file diff --git a/SharpMath/Geometry/VariableMatrix.cs b/SharpMath/Geometry/VariableMatrix.cs new file mode 100644 index 0000000..1400863 --- /dev/null +++ b/SharpMath/Geometry/VariableMatrix.cs @@ -0,0 +1,62 @@ +using System; +using System.ComponentModel; + +namespace SharpMath.Geometry +{ + /// + /// Represents a matrix that can vary in its column and row count. + /// + internal sealed class VariableMatrix : IMatrix + { + private readonly double[,] _fields; + + /// + /// Initializes a new instance of the class. + /// + /// The row count of the . + /// The column count of the . + public VariableMatrix(uint rowCount, uint columnCount) + { + _fields = new double[rowCount, columnCount]; + RowCount = rowCount; + ColumnCount = columnCount; + } + + /// + /// Gets or sets the field value at the specified row and column indices. + /// + /// The row index. + /// The column index. + /// The field value at the specified row and column indices. + public double this[uint row, uint column] + { + get { return _fields[row, column]; } + set { _fields[row, column] = value; } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Simple indexer is not supported in this class.", true)] + public double this[uint index] + { + get + { + return double.NaN; + } + + set + { + return; + } + } + + /// + /// Gets the column count of the . + /// + public uint ColumnCount { get; } + + /// + /// Gets the row count of the . + /// + public uint RowCount { get; } + } +} \ No newline at end of file diff --git a/SharpMath/Geometry/Vector.cs b/SharpMath/Geometry/Vector.cs deleted file mode 100644 index 0fc7220..0000000 --- a/SharpMath/Geometry/Vector.cs +++ /dev/null @@ -1,696 +0,0 @@ -// Author: Dominic Beger (Trade/ProgTrade) 2016 -// Improvements: Stefan Baumann 2016 - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using SharpMath.Geometry.Exceptions; - -namespace SharpMath.Geometry -{ - /// - /// Represents a vector. - /// - public class Vector : IEnumerable, IEquatable - { - // ReSharper disable once InconsistentNaming - protected readonly double[] _coordinateValues; - - /// - /// Initializes a new instance of the class. - /// - /// The dimension of the . - public Vector(uint dimension) - { - Dimension = dimension; - _coordinateValues = new double[Dimension]; - } - - /// - /// Initializes a new instance of the class. - /// - /// The coordinate values of the . - public Vector(params double[] coordinateValues) - { - Dimension = (uint) coordinateValues.Length; - _coordinateValues = coordinateValues; - } - - /// - /// Initializes a new instance of the class. - /// - /// The existing to copy. - public Vector(Vector vector) - { - Dimension = vector.Dimension; - _coordinateValues = new double[Dimension]; - for (uint i = 0; i < vector.Dimension; ++i) - this[i] = vector[i]; - } - - /// - /// Gets or sets the value of the coordinate at the specified index. - /// - /// The index to use. - /// Returns the value of the coordinate. - public double this[uint index] - { - get - { - if (index >= Dimension) - throw new IndexOutOfRangeException($"index must be between 0 and {Dimension}."); - return _coordinateValues[index]; - } - - set - { - if (index >= Dimension) - throw new IndexOutOfRangeException($"index must be between 0 and {Dimension}."); - _coordinateValues[index] = value; - } - } - - /// - /// Gets the dimension of this . - /// - public uint Dimension { get; } - - /// - /// Gets the length of this . - /// - public double Magnitude => Math.Sqrt(SquareMagnitude); - - /// - /// Gets the squared length of this . - /// - public double SquareMagnitude - { - get - { - var result = 0d; - for (uint i = 0; i < Dimension; ++i) - result += Math.Pow(this[i], 2); - return result; - } - } - - /// - /// Gets a value indicating whether this is normalized, or not. - /// - public bool IsNormalized => FloatingNumber.AreApproximatelyEqual(Magnitude, 1); - - private bool IsZeroVector - { - get { return this.All(c => FloatingNumber.AreApproximatelyEqual(c, 0)); } - } - - /// - /// Returns an enumerator that iterates through the collection of coordinates. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - for (int i = 0; i < this.Dimension; i++) - { - yield return this[(uint)i]; - } - yield break; - } - - /// - /// Returns an enumerator that iterates through the collection of coordinates. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i < this.Dimension; i++) - { - yield return this[(uint)i]; - } - yield break; - } - - public bool Equals(Vector other) - { - if (ReferenceEquals(null, other)) - return false; - - return this == other; - } - - /// - /// Represents this as a horizontal whose column count equals its - /// dimension. - /// - /// The represented as horizontal . - public Matrix AsHorizontalMatrix() - { - var matrix = new Matrix(1, Dimension); - for (uint i = 0; i < Dimension; ++i) - matrix[0, i] = this[i]; - return matrix; - } - - /// - /// Represents this as a vertical whose row count equals its dimension. - /// - /// The represented as vertical . - public Matrix AsVerticalMatrix() - { - var matrix = new Matrix(Dimension, 1); - for (uint i = 0; i < Dimension; ++i) - matrix[i, 0] = this[i]; - return matrix; - } - - /// - /// Calculates the scalar product of this and the specified . - /// - /// The other that should be included into the calculation. - /// Returns the calculated scalar as a . - /// The dimensions of the vectors do not equal each other. - public double ScalarProduct(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - double result = 0; - for (uint i = 0; i < Dimension; ++i) - result += this[i]*other[i]; - return result; - } - - /// - /// Calculates the scalar product of the specified instances. - /// - /// The first that should be included into the calculation. - /// The second that should be included into the calculation. - /// Returns the calculated scalar as a . - /// The dimensions of the vectors do not equal each other. - public static double ScalarProduct(Vector firstVector, Vector secondVector) - { - return firstVector.ScalarProduct(secondVector); - } - - /// - /// Linearly interpolates between two s. - /// - /// The source point. - /// The target point. - /// The fraction. - /// The position of the new point. - /// The dimensions of the vectors do not equal each other. - public static Vector Lerp(Vector source, Vector target, double fraction) - { - if (source.Dimension != target.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - if (fraction > 1) - fraction = 1; - else if (fraction < 0) - fraction = 0; - return LerpUnclamped(source, target, fraction); - } - - /// - /// Linearly interpolates between two vectors. - /// - /// The source point. - /// The target point. - /// The fraction. - /// The position of the new point. - /// The dimensions of the vectors do not equal each other. - public static Vector LerpUnclamped(Vector source, Vector target, double fraction) - { - if (source.Dimension != target.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - // source + (target - source) * fraction - return Add(source, Multiply(Subtract(target, source), fraction)); - } - - /// - /// Moves this source point in a straight line towards a target point by adding the given distance delta and returns - /// its new position. - /// - /// The target point. - /// The distance delta that this source point is moved by in all directions. - /// The position of the new point. - /// The dimensions of the vectors do not equal each other. - public Vector MoveTowards(Vector target, double maxDistanceDelta) - { - if (Dimension != target.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - return LerpUnclamped(this, target, (maxDistanceDelta/DistanceTo(target))); - } - - /// - /// Moves this source point in a straight line towards a target point by adding the given distance delta. - /// - /// The target point. - /// The distance delta that this source point is moved by in all directions. - /// The dimensions of the vectors do not equal each other. - public void MoveTowardsPoint(Vector target, double maxDistanceDelta) - { - if (Dimension != target.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - var newPosition = LerpUnclamped(this, target, (maxDistanceDelta/DistanceTo(target))); - for (uint i = 0; i < newPosition.Dimension; ++i) - this[i] = newPosition[i]; - } - - /// - /// Moves a source point in a straight line towards a target point by adding the given distance delta and returns its - /// new position. - /// - /// The source point. - /// The target point. - /// The distance delta that the source point is moved by in all directions. - /// The new position of the point as . - /// The dimensions of the vectors do not equal each other. - public static Vector MoveTowards(Vector source, Vector target, double maxDistanceDelta) - { - if (source.Dimension != target.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - return LerpUnclamped(source, target, (maxDistanceDelta/source.DistanceTo(target))); - } - - /// - /// Calculates the angle between this and the specified instance. - /// - /// The other vector. - /// The angle between this and the specified instance. - /// The dimensions of the vectors do not equal each other. - public double Angle(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - // Prevent a DivideByZeroException as at least one of the vectors could be the zero vector. - if (IsZeroVector || other.IsZeroVector) - throw new InvalidOperationException( - "The angle of two vectors cannot be calculated, if at least one equals the zero vector."); - - return Math.Acos((ScalarProduct(other)/(Magnitude*other.Magnitude))); - } - - /// - /// Calculates the angle between two instances. - /// - /// The first . - /// The second . - /// The angle between the instances. - /// The dimensions of the vectors do not equal each other. - public static double Angle(Vector firstVector, Vector secondVector) - { - return firstVector.Angle(secondVector); - } - - /// - /// Calculates the distance between this and the specified point. - /// - /// The other point. - /// The distance between this and the specified point. - public double DistanceTo(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - return Subtract(other, this).Magnitude; - } - - /// - /// Calculates the distance between two points. - /// - /// The source point. - /// The target point. - /// The distance betweet the this and the specified point. - public static double Distance(Vector source, Vector target) - { - return source.DistanceTo(target); - } - - /// - /// Determines whether this is orthogonal to another one, or not. - /// - /// The other . - /// true if this is orthogonal to another one, otherwise false. - public bool IsOrthogonalTo(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - return !IsZeroVector && !other.IsZeroVector && FloatingNumber.AreApproximatelyEqual(ScalarProduct(other), 0); - } - - /// - /// Determines whether two instances are orthogonal to each other, or not. - /// - /// The first . - /// The second . - /// true if the instances are orthogonal to each other, otherwise false. - public static bool AreOrthogonal(Vector firstVector, Vector secondVector) - { - return firstVector.IsOrthogonalTo(secondVector); - } - - /// - /// Determines whether this is orthonormal to another one, or not. - /// - /// The other . - /// true if this is orthonormal to another one, otherwise false. - public bool IsOrthonormalTo(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - return IsOrthogonalTo(other) && IsNormalized && other.IsNormalized; - } - - /// - /// Determines whether two instances are orthonormal to each other, or not. - /// - /// The first . - /// The second . - /// true if the instances are orthonormal to each other, otherwise false. - public static bool AreOrthonormal(Vector firstVector, Vector secondVector) - { - return firstVector.IsOrthonormalTo(secondVector); - } - - /// - /// Determines whether this is orthonormal to another one, or not. - /// - /// The other . - /// true if this is parallel to another one, otherwise false. - public bool IsParallelTo(Vector other) - { - if (Dimension != other.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - if (IsZeroVector || other.IsZeroVector) - return false; - - double firstResult = 0; - for (uint i = 0; i < Dimension; ++i) - { - if (i == 0) - firstResult = other[i]/this[i]; - else - { - if (!FloatingNumber.AreApproximatelyEqual(other[i]/this[i], firstResult)) - return false; - } - } - - return true; - } - - /// - /// Determines whether two instances are parallel to each other, or not. - /// - /// The first . - /// The second . - /// true if the instances are parallel to each other, otherwise false. - public bool AreParallel(Vector firstVector, Vector secondVector) - { - return firstVector.IsParallelTo(secondVector); - } - - /// - /// Calculates the negated of this . - /// - /// The negated of this . - public Vector Negate() - { - var resultVector = new Vector(this); - for (uint i = 0; i < Dimension; ++i) - resultVector[i] = -this[i]; - return resultVector; - } - - /// - /// Calculates the normalized of this . - /// - /// The normalized . - public Vector Normalize() - { - if (IsZeroVector) - throw new InvalidOperationException("A zero vector cannot be normalized."); - - var resultVector = new Vector(this); - for (uint i = 0; i < Dimension; ++i) - resultVector[i] = this[i]/Magnitude; - return resultVector; - } - - /// - /// Converts this into a of another dimension. - /// - /// The type that the current should be converted to. - /// This converted into the given type. - public T Convert() where T : Vector, new() - // Type parameter because we need to create an instance of that specific type - { - var resultVector = new T(); - if (resultVector.Dimension == Dimension) - Debug.Print( - $"Vector conversion method (Vector{Dimension}.To()) is currently used to convert a vector into one of the same dimension. Please check if this has been your intention."); - for (uint i = 0; i < Math.Min(Dimension, resultVector.Dimension); ++i) - resultVector[i] = this[i]; - return resultVector; - } - - /// - /// Adds two instances. - /// - /// The first . - /// The second . - /// The resulting . - public static Vector Add(Vector firstVector, Vector secondVector) - { - if (firstVector.Dimension != secondVector.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - var resultVector = new Vector(firstVector.Dimension); - for (uint i = 0; i < resultVector.Dimension; ++i) - resultVector[i] = firstVector[i] + secondVector[i]; - return resultVector; - } - - /// - /// Subtracts two instances. - /// - /// The first . - /// The second . - /// The resulting . - public static Vector Subtract(Vector firstVector, Vector secondVector) - { - if (firstVector.Dimension != secondVector.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - var resultVector = new Vector(firstVector.Dimension); - for (uint i = 0; i < resultVector.Dimension; ++i) - resultVector[i] = firstVector[i] - secondVector[i]; - return resultVector; - } - - /// - /// Multiplies a with a specified scalar. - /// - /// The . - /// The scalar. - /// The resulting . - public static Vector Multiply(Vector vector, double scalar) - { - var resultVector = new Vector(vector.Dimension); - for (uint i = 0; i < resultVector.Dimension; ++i) - resultVector[i] = vector[i]*scalar; - return resultVector; - } - - /// - /// Divides a by multipling it with the reciprocal of the scalar. - /// - /// The . - /// The scalar whose reciprocal will be calculated. - /// The resulting . - public static Vector Divide(Vector vector, double scalar) - { - var resultVector = new Vector(vector.Dimension); - for (uint i = 0; i < resultVector.Dimension; ++i) - resultVector[i] = vector[i]*(1/scalar); - return resultVector; - } - - /// - /// Implements the operator +. - /// - /// The first . - /// The second . - /// - /// The resulting . - /// - public static Vector operator +(Vector firstVector, Vector secondVector) - { - return Add(firstVector, secondVector); - } - - /// - /// Implements the operator -. - /// - /// The first . - /// The second . - /// - /// The resulting . - /// - public static Vector operator -(Vector firstVector, Vector secondVector) - { - return Subtract(firstVector, secondVector); - } - - /// - /// Implements the operator -. - /// - /// The to negate. - /// - /// The negated . - /// - public static Vector operator -(Vector current) - { - return current.Negate(); - } - - /// - /// Implements the operator *. - /// - /// The . - /// The scalar. - /// - /// The resulting . - /// - public static Vector operator *(Vector vector, double scalar) - { - return Multiply(vector, scalar); - } - - /// - /// Implements the operator * for calculating the scalar product of two instances. - /// - /// The first . - /// The second . - /// - /// The scalar that has been calculated. - /// - public static double operator *(Vector firstVector, Vector secondVector) - { - return ScalarProduct(firstVector, secondVector); - } - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - - return this == obj as Vector; - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - unchecked - { - int hash = 17; - for (uint i = 0; i < Dimension; ++i) - hash = hash*23 + this[i].GetHashCode(); - return hash; - } - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public T Clone() where T : Vector, new() - { - var cloneVector = new T(); - for (uint i = 0; i < Dimension; ++i) - cloneVector[i] = this[i]; - return cloneVector; - } - - /// - /// Implements the operator ==. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator ==(Vector left, Vector right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.Dimension != right.Dimension) - return false; - - for (uint i = 0; i < left.Dimension; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; - } - - /// - /// Implements the operator !=. - /// - /// The left . - /// The right . - /// - /// The result of the operator. - /// - public static bool operator !=(Vector left, Vector right) - { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - if (left.Dimension != right.Dimension) - return true; - - for (uint i = 0; i < left.Dimension; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/SharpMath/Geometry/Vector2.cs b/SharpMath/Geometry/Vector2.cs index d2426d7..6d66d19 100644 --- a/SharpMath/Geometry/Vector2.cs +++ b/SharpMath/Geometry/Vector2.cs @@ -1,31 +1,28 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 -using System; using SharpMath.Geometry.Exceptions; +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; namespace SharpMath.Geometry { /// /// Represents a two-dimensional vector. /// - public class Vector2 : Vector + /// + [Serializable] + public struct Vector2 : IVector, IEquatable, IEnumerable { - /// - /// Initializes a new instance of the class. - /// - public Vector2() - : base(2) - { - // We don't need to set anything as value types are initialized by default with the values we want - } - /// /// Initializes a new instance of the class. /// /// The existing to copy. public Vector2(Vector2 vector) - : base(vector) { + X = vector.X; + Y = vector.Y; } /// @@ -34,8 +31,9 @@ public Vector2(Vector2 vector) /// The value of the X-coordinate (X1 in mathematic coordinate systems). /// The value of the Y-coordinate (X2 in mathematic coordinate systems). public Vector2(double x, double y) - : base(x, y) { + X = x; + Y = y; } /// @@ -43,7 +41,7 @@ public Vector2(double x, double y) /// /// The that a position should be created for. public Vector2(Point2D point) - : base(point.PositionVector) + : this(point.PositionVector) { } @@ -53,26 +51,45 @@ public Vector2(Point2D point) /// The tail of the . /// The head of the . public Vector2(Point2D bottom, Point2D tip) - : base((tip - bottom).PositionVector) + : this((tip - bottom).PositionVector) { } /// /// Gets or sets the value of the X-coordinate (X1 in mathematic coordinate systems). /// - public double X - { - get { return this[0]; } - set { this[0] = value; } - } + public double X { get; set; } /// /// Gets or sets the value of the Y-coordinate (X2 in mathematic coordinate systems). /// - public double Y + public double Y { get; set; } + + /// + /// Gets or sets the value of the coordinate at the specified index. + /// + /// The index. + /// The value of the coordinate at the specified index. + public double this[uint index] { - get { return this[1]; } - set { this[1] = value; } + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + default: throw new IndexOutOfRangeException("The index must be between 0 and 1."); + } + } + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 1."); + } + } } /// @@ -116,9 +133,43 @@ public double Y public static Vector2 UnitY => new Vector2(0, 1); /// - /// Gets the that is perpendicular to this . + /// Gets the that is perpendicular to the . + /// + public Vector2 Perpendicular => new Vector2(-Y, X); + + /// + /// Gets the dimension of the . + /// + public uint Dimension => 2; + + /// + /// Gets the length of the . + /// + public double Magnitude => Math.Sqrt(SquareMagnitude); + + /// + /// Gets the squared length of the . + /// + public double SquareMagnitude + { + get + { + double result = 0; + for (uint i = 0; i < 2; ++i) + result += Math.Pow(this[i], 2); + return result; + } + } + + /// + /// Gets a value indicating whether the is normalized, or not. + /// + public bool IsNormalized => Magnitude.IsApproximatelyEqualTo(1); + + /// + /// Gets a value indicating whether the has all of its components set to zero, or not. /// - public Vector2 CrossProduct => new Vector2(Y, -X); + public bool IsZero => this.All(c => FloatingNumber.AreApproximatelyEqual(c, 0)); /// /// Gets the LaTeX-string representing this vector graphically. @@ -126,16 +177,114 @@ public double Y public string ToLaTeXString() => @"\left( \begin{array}{c} " + this[0] + @" \\ " + this[1] + @" \end{array} \right)"; /// - /// Generates a from the base class, if the dimension is correct. + /// Generates a from an object that implements the interface, if the dimension is correct. /// - /// The to generate a from. + /// The to generate a from. /// The generated . - /// The dimension of the given vector is invalid. It must be 2. - public static Vector2 FromVector(Vector vector) + /// The dimension of the given vector is invalid. It must be 2. + public static Vector2 FromVector(IVector vector) { if (vector.Dimension != 2) - throw new ArgumentException("The dimension of the given vector is invalid. It must be 2."); - return new Vector2(vector[0], vector[1]); + throw new DimensionException("The dimension of the given vector is invalid. It must be 2."); + return new Vector2(vector[0], vector[1] ); + } + + /// + /// Calculates the angle between two instances. + /// + /// The first . + /// The second . + /// The angle between the instances. + public static double Angle(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.Angle(secondVector); + } + + /// + /// Calculates the area of the parallelogram that this and the specified instances span. + /// + /// The other . + /// The area of the spanned parallelogram. + public double Area(Vector2 other) + { + return Math.Abs(VectorProduct(other)); + } + + /// + /// Calculates the area of the parallelogram that the two specified instances span. + /// + /// The first . + /// The second . + /// The area of the spanned parallelogram. + public static double Area(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.Area(secondVector); + } + + /// + /// Determines whether two instances are orthogonal to each other, or not. + /// + /// The first . + /// The second . + /// true, if the instances are orthogonal to each other, otherwise false. + public static bool AreOrthogonal(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.CheckForOrthogonality(secondVector); + } + + /// + /// Determines whether two instances are orthonormal to each other, or not. + /// + /// The first . + /// The second . + /// true if the instances are orthonormal to each other, otherwise false. + public static bool AreOrthonormal(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.CheckForOrthonormality(secondVector); + } + + /// + /// Determines whether two instances are parallel to each other, or not. + /// + /// The first . + /// The second . + /// true if the instances are parallel to each other, otherwise false. + public static bool AreParallel(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.CheckForParallelism(secondVector); + } + + /// + /// Calculates the distance between two points. + /// + /// The source point. + /// The target point. + /// The distance between specified points. + public static double Distance(Vector2 source, Vector2 target) + { + return source.Distance(target); + } + + /// + /// Divides a by multipling it with the reciprocal of the scalar. + /// + /// The . + /// The scalar whose reciprocal will be calculated. + /// The resulting . + public static Vector2 Divide(Vector2 vector, double scalar) + { + return vector * (1/scalar); + } + + /// + /// Calculates the dot product of the specified instances. + /// + /// The first that should be included into the calculation. + /// The second that should be included into the calculation. + /// The calculated scalar as a . + public static double DotProduct(Vector2 firstVector, Vector2 secondVector) + { + return firstVector.DotProduct(secondVector); } /// @@ -145,10 +294,9 @@ public static Vector2 FromVector(Vector vector) /// The target point. /// The fraction. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector2 Lerp(Vector2 source, Vector2 target, double fraction) { - return FromVector(Vector.Lerp(source, target, fraction)); + return VectorUtils.Lerp(source, target, fraction); } /// @@ -158,10 +306,9 @@ public static Vector2 Lerp(Vector2 source, Vector2 target, double fraction) /// The target point. /// The fraction. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector2 LerpUnclamped(Vector2 source, Vector2 target, double fraction) { - return FromVector(Vector.LerpUnclamped(source, target, fraction)); + return VectorUtils.LerpUnclamped(source, target, fraction); } /// @@ -172,33 +319,30 @@ public static Vector2 LerpUnclamped(Vector2 source, Vector2 target, double fract /// The target point. /// The distance delta that the source point is moved by in all directions. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector2 MoveTowards(Vector2 source, Vector2 target, double maxDistanceDelta) { - return FromVector(Vector.MoveTowards(source, target, maxDistanceDelta)); + return VectorUtils.MoveTowards(source, target, maxDistanceDelta); } /// - /// Calculates the area of the parallelogram that this and the specified instances span. + /// Calculates the vector product of the current and the specified instance. /// - /// The other . - /// The area of the spanned parallelogram. - public double Area(Vector2 other) + /// The other . + /// The vector product of the current and the specified . + public double VectorProduct(Vector2 other) { - if (this == Zero || other == Zero) - return 0; - return Magnitude*Math.Sin(Angle(other))*other.Magnitude; + return (X * other.Y) - (Y * other.X); } /// - /// Calculates the area of the parallelogram that the two specified instances span. + /// Calculates the vector product of the specified instances. /// - /// The first . - /// The second . - /// The area of the spanned parallelogram. - public static double Area(Vector2 firstVector, Vector2 secondVector) + /// The first . + /// The second . + /// The vector product of the specified instances. + public static double VectorProduct(Vector2 firstVector, Vector2 secondVector) { - return firstVector.Area(secondVector); + return firstVector.VectorProduct(secondVector); } /// @@ -211,7 +355,7 @@ public static double Area(Vector2 firstVector, Vector2 secondVector) /// public static Vector2 operator +(Vector2 firstVector, Vector2 secondVector) { - return FromVector(Add(firstVector, secondVector)); + return VectorUtils.Add(firstVector, secondVector); } /// @@ -224,7 +368,7 @@ public static double Area(Vector2 firstVector, Vector2 secondVector) /// public static Vector2 operator -(Vector2 firstVector, Vector2 secondVector) { - return FromVector(Subtract(firstVector, secondVector)); + return VectorUtils.Subtract(firstVector, secondVector); } /// @@ -236,7 +380,7 @@ public static double Area(Vector2 firstVector, Vector2 secondVector) /// public static Vector2 operator -(Vector2 current) { - return FromVector(current.Negate()); + return current.Negate(); } /// @@ -249,11 +393,11 @@ public static double Area(Vector2 firstVector, Vector2 secondVector) /// public static Vector2 operator *(Vector2 vector, double scalar) { - return FromVector(Multiply(vector, scalar)); + return VectorUtils.Multiply(vector, scalar); } /// - /// Implements the operator * for calculating the scalar product of two instances. + /// Implements the operator * for calculating the dot product of two instances. /// /// The first . /// The second . @@ -262,7 +406,7 @@ public static double Area(Vector2 firstVector, Vector2 secondVector) /// public static double operator *(Vector2 firstVector, Vector2 secondVector) { - return ScalarProduct(firstVector, secondVector); + return VectorUtils.DotProduct(firstVector, secondVector); } /// @@ -305,27 +449,21 @@ public static Vector2 Transform(Matrix3x3 matrix, Vector2 vector) /// public override string ToString() { - return $"X: {this[0]}, Y: {this[1]}"; + return $"Vector2 {{X: {this[0]}, Y: {this[1]}}}"; } /// - /// Determines whether the specified , is equal to this instance. + /// Determines whether the specified , is equal to the current instance. /// - /// The to compare with this instance. + /// The to compare with the current instance. /// - /// true if the specified is equal to this instance; otherwise, false. + /// true if the specified is equal to the current instance; otherwise, false. /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() == typeof (Vector2)) return this == (Vector2) obj; - var vector = obj as Vector; + var vector = obj as IVector; if (Dimension != vector?.Dimension) return false; return this == FromVector(vector); @@ -348,6 +486,29 @@ public override int GetHashCode() } } + public bool Equals(Vector2 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + for (uint i = 0; i < 2; i++) + { + yield return this[i]; + } + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (uint i = 0; i < 2; i++) + { + yield return this[i]; + } + yield break; + } + /// /// Implements the operator ==. /// @@ -358,16 +519,7 @@ public override int GetHashCode() /// public static bool operator ==(Vector2 left, Vector2 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 2; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return left.SequenceEqual(right); } /// @@ -380,16 +532,7 @@ public override int GetHashCode() /// public static bool operator !=(Vector2 left, Vector2 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 2; ++i) - { - if (FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/Vector3.cs b/SharpMath/Geometry/Vector3.cs index 8208892..405b38d 100644 --- a/SharpMath/Geometry/Vector3.cs +++ b/SharpMath/Geometry/Vector3.cs @@ -1,31 +1,27 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; -using SharpMath.Geometry.Exceptions; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { /// /// Represents a three-dimensional vector. /// - public class Vector3 : Vector + [Serializable] + public struct Vector3 : IVector, IEquatable, IEnumerable { - /// - /// Initializes a new instance of the class. - /// - public Vector3() - : base(3) - { - // We don't need to set anything as value types are initialized by default with the values we want - } - /// /// Initializes a new instance of the class. /// /// The existing to copy. public Vector3(Vector3 vector) - : base(vector) { + X = vector.X; + Y = vector.Y; + Z = vector.Z; } /// @@ -35,8 +31,10 @@ public Vector3(Vector3 vector) /// The value of the Y-coordinate (X3 in mathematic coordinate systems). /// The value of the Z-coordinate (X1 in mathematic coordinate systems). public Vector3(double x, double y, double z) - : base(x, y, z) { + X = x; + Y = y; + Z = z; } /// @@ -44,8 +42,10 @@ public Vector3(double x, double y, double z) /// /// The that a position should be created for. public Vector3(Point3D point) - : base(point.PositionVector) { + X = point.X; + Y = point.Y; + Z = point.Z; } /// @@ -54,35 +54,52 @@ public Vector3(Point3D point) /// The tail of the . /// The head of the . public Vector3(Point3D bottom, Point3D tip) - : base((tip - bottom).PositionVector) + : this((tip - bottom).PositionVector) { } /// /// Gets or sets the value of the X-coordinate (X2 in mathematic coordinate systems). /// - public double X - { - get { return this[0]; } - set { this[0] = value; } - } + public double X { get; set; } /// /// Gets or sets the value of the Y-coordinate (X3 in mathematic coordinate systems). /// - public double Y - { - get { return this[1]; } - set { this[1] = value; } - } + public double Y { get; set; } /// /// Gets or sets the value of the Z-coordinate (X1 in mathematic coordinate systems). /// - public double Z + public double Z { get; set; } + + /// + /// Gets or sets the value of the coordinate at the specified index. + /// + /// The index. + /// The value of the coordinate at the specified index. + public double this[uint index] { - get { return this[2]; } - set { this[2] = value; } + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 2."); + } + } } /// @@ -140,6 +157,40 @@ public double Z /// public static Vector3 UnitZ => new Vector3(0, 0, 1); + /// + /// Gets the dimension of the . + /// + public uint Dimension => 3; + + /// + /// Gets the length of the . + /// + public double Magnitude => Math.Sqrt(SquareMagnitude); + + /// + /// Gets the squared length of the . + /// + public double SquareMagnitude + { + get + { + double result = 0; + for (uint i = 0; i < 3; ++i) + result += Math.Pow(this[i], 2); + return result; + } + } + + /// + /// Gets a value indicating whether the is normalized, or not. + /// + public bool IsNormalized => Magnitude.IsApproximatelyEqualTo(1); + + /// + /// Gets a value indicating whether the has all of its components set to zero, or not. + /// + public bool IsZero => this.All(c => FloatingNumber.AreApproximatelyEqual(c, 0)); + /// /// Gets the LaTeX-string representing this vector graphically. /// @@ -147,12 +198,12 @@ public string ToLaTeXString() => @"\left( \begin{array}{c} " + this[0] + @" \\ " + this[1] + @" \\ " + this[2] + @" \end{array} \right)"; /// - /// Generates a from the base class, if the dimension is correct. + /// Generates a from an object implementing the interface, if the dimension is correct. /// - /// The to generate a from. + /// The to generate a from. /// The generated . /// The dimension of the given vector is invalid. It must be 3. - public static Vector3 FromVector(Vector vector) + public static Vector3 FromVector(IVector vector) { if (vector.Dimension != 3) throw new ArgumentException("The dimension of the given vector is invalid. It must be 3."); @@ -160,24 +211,24 @@ public static Vector3 FromVector(Vector vector) } /// - /// Calculates the that is perpendicular to this and the specified . + /// Calculates the area of the parallelogram that this and the specified instances span. /// - /// The other that should be included into the calculation. - /// Returns the calculated . - public Vector3 CrossProduct(Vector3 other) + /// The other . + /// The area of the spanned parallelogram. + public double Area(Vector3 other) { - return new Vector3((Y*other.Z - Z*other.Y), (Z*other.X - X*other.Z), (X*other.Y - Y*other.X)); + return VectorProduct(this, other).Magnitude; } /// - /// Calculates the that is perpendicular to the specified instances. + /// Calculates the area of the parallelogram that the two specified instances span. /// - /// The first that should be included into the calculation. - /// The second that should be included into the calculation. - /// Returns the calculated . - public static Vector3 CrossProduct(Vector3 firstVector, Vector3 secondVector) + /// The first . + /// The second . + /// The area of the spanned parallelogram. + public static double Area(Vector3 firstVector, Vector3 secondVector) { - return firstVector.CrossProduct(secondVector); + return firstVector.Area(secondVector); } /// @@ -187,10 +238,9 @@ public static Vector3 CrossProduct(Vector3 firstVector, Vector3 secondVector) /// The target point. /// The fraction. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector3 Lerp(Vector3 source, Vector3 target, double fraction) { - return FromVector(Vector.Lerp(source, target, fraction)); + return VectorUtils.Lerp(source, target, fraction); } /// @@ -200,10 +250,9 @@ public static Vector3 Lerp(Vector3 source, Vector3 target, double fraction) /// The target point. /// The fraction. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector3 LerpUnclamped(Vector3 source, Vector3 target, double fraction) { - return FromVector(Vector.LerpUnclamped(source, target, fraction)); + return VectorUtils.LerpUnclamped(source, target, fraction); } /// @@ -214,31 +263,9 @@ public static Vector3 LerpUnclamped(Vector3 source, Vector3 target, double fract /// The target point. /// The distance delta that the source point is moved by in all directions. /// The position of the new point. - /// The dimensions of the vectors do not equal each other. public static Vector3 MoveTowards(Vector3 source, Vector3 target, double maxDistanceDelta) { - return FromVector(Vector.MoveTowards(source, target, maxDistanceDelta)); - } - - /// - /// Calculates the area of the parallelogram that this and the specified instances span. - /// - /// The other . - /// The area of the spanned parallelogram. - public double Area(Vector3 other) - { - return CrossProduct(this, other).Magnitude; - } - - /// - /// Calculates the area of the parallelogram that the two specified instances span. - /// - /// The first . - /// The second . - /// The area of the spanned parallelogram. - public static double Area(Vector3 firstVector, Vector3 secondVector) - { - return firstVector.Area(secondVector); + return VectorUtils.MoveTowards(source, target, maxDistanceDelta); } /// @@ -254,7 +281,28 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect //var matrix = SquareMatrix.FromMatrix(firstVector.AsHorizontalMatrix().AugmentVertically(secondVector.AsHorizontalMatrix()).AugmentVertically(thirdVector.AsHorizontalMatrix())); //return Math.Abs(matrix.Determinant); - return Math.Abs(ScalarProduct(CrossProduct(firstVector, secondVector), thirdVector)); + return Math.Abs(VectorUtils.DotProduct(VectorProduct(firstVector, secondVector), thirdVector)); + } + + /// + /// Calculates the vector product of the current and the specified . + /// + /// The other . + /// The calculated . + public Vector3 VectorProduct(Vector3 other) + { + return new Vector3((Y * other.Z - Z * other.Y), (Z * other.X - X * other.Z), (X * other.Y - Y * other.X)); + } + + /// + /// Calculates the that is perpendicular to the specified instances. + /// + /// The first that should be included into the calculation. + /// The second that should be included into the calculation. + /// Returns the calculated . + public static Vector3 VectorProduct(Vector3 firstVector, Vector3 secondVector) + { + return firstVector.VectorProduct(secondVector); } /// @@ -267,7 +315,7 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect /// public static Vector3 operator +(Vector3 firstVector, Vector3 secondVector) { - return FromVector(Add(firstVector, secondVector)); + return VectorUtils.Add(firstVector, secondVector); } /// @@ -280,7 +328,7 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect /// public static Vector3 operator -(Vector3 firstVector, Vector3 secondVector) { - return FromVector(Subtract(firstVector, secondVector)); + return VectorUtils.Subtract(firstVector, secondVector); } /// @@ -292,7 +340,7 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect /// public static Vector3 operator -(Vector3 current) { - return FromVector(current.Negate()); + return current.Negate(); } /// @@ -305,7 +353,7 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect /// public static Vector3 operator *(Vector3 vector, double scalar) { - return FromVector(Multiply(vector, scalar)); + return VectorUtils.Multiply(vector, scalar); } /// @@ -318,7 +366,7 @@ public static double ScalarTripleProduct(Vector3 firstVector, Vector3 secondVect /// public static double operator *(Vector3 firstVector, Vector3 secondVector) { - return ScalarProduct(firstVector, secondVector); + return VectorUtils.DotProduct(firstVector, secondVector); } /// @@ -356,15 +404,9 @@ public override string ToString() /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() == typeof (Vector3)) return this == (Vector3) obj; - var vector = obj as Vector; + var vector = obj as IVector; if (Dimension != vector?.Dimension) return false; return this == FromVector(vector); @@ -388,6 +430,29 @@ public override int GetHashCode() } } + public bool Equals(Vector3 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + for (uint i = 0; i < 3; i++) + { + yield return this[i]; + } + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (uint i = 0; i < 3; i++) + { + yield return this[i]; + } + yield break; + } + /// /// Implements the operator ==. /// @@ -398,16 +463,7 @@ public override int GetHashCode() /// public static bool operator ==(Vector3 left, Vector3 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 3; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return left.SequenceEqual(right); } /// @@ -420,16 +476,7 @@ public override int GetHashCode() /// public static bool operator !=(Vector3 left, Vector3 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 3; ++i) - { - if (FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/Vector4.cs b/SharpMath/Geometry/Vector4.cs index aaebceb..28c19a4 100644 --- a/SharpMath/Geometry/Vector4.cs +++ b/SharpMath/Geometry/Vector4.cs @@ -1,21 +1,17 @@ // Author: Dominic Beger (Trade/ProgTrade) 2016 using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace SharpMath.Geometry { /// /// Represents a four-dimensional vector with a homogeneous coordinate. /// - public class Vector4 : Vector + public struct Vector4 : IVector, IEquatable, IEnumerable { - /// - /// Initializes a new instance of the class. - /// - public Vector4() : base(4) - { - } - /// /// Initializes a new instance of the class. /// @@ -24,8 +20,11 @@ public Vector4() : base(4) /// The value of the Z-coordinate (X1 in mathematic coordinate systems). /// The value of the homogeneous coordinate. public Vector4(double x, double y, double z, double w) - : base(x, y, z, w) { + X = x; + Y = y; + Z = z; + W = w; } /// @@ -33,48 +32,66 @@ public Vector4(double x, double y, double z, double w) /// /// The existing to copy. public Vector4(Vector4 vector) - : base(vector) { + X = vector.X; + Y = vector.Y; + Z = vector.Z; + W = vector.W; } /// - /// Gets or sets the value of the X-coordinate (X2 in mathematic coordinate systems). + /// Gets or sets the value of the coordinate at the specified index. /// - public double X + /// The index. + /// The value of the coordinate at the specified index. + public double this[uint index] { - get { return this[0]; } - set { this[0] = value; } + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + case 3: return W; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + case 3: W = value; break; + default: throw new IndexOutOfRangeException("The index must be between 0 and 3."); + } + } } + /// + /// Gets or sets the value of the X-coordinate (X2 in mathematic coordinate systems). + /// + public double X { get; set; } + /// /// Gets or sets the value of the Y-coordinate (X3 in mathematic coordinate systems). /// - public double Y - { - get { return this[1]; } - set { this[1] = value; } - } + public double Y { get; set; } /// /// Gets or sets the value of the Z-coordinate (X1 in mathematic coordinate systems). /// - public double Z - { - get { return this[2]; } - set { this[2] = value; } - } + public double Z { get; set; } /// /// Gets or sets the value of the homogenous coordinate. /// - public double W - { - get { return this[3]; } - set { this[3] = value; } - } + public double W { get; set; } /// - /// A unit with all values set to zero. + /// A with all values set to zero. /// public static Vector4 Zero => new Vector4(); @@ -103,6 +120,40 @@ public double W /// public static Vector4 UnitW => new Vector4(0, 0, 1, 0); + /// + /// Gets the dimension of the . + /// + public uint Dimension => 4; + + /// + /// Gets the length of the . + /// + public double Magnitude => Math.Sqrt(SquareMagnitude); + + /// + /// Gets the squared length of the . + /// + public double SquareMagnitude + { + get + { + double result = 0; + for (uint i = 0; i < 4; ++i) + result += Math.Pow(this[i], 2); + return result; + } + } + + /// + /// Gets a value indicating whether the is normalized, or not. + /// + public bool IsNormalized => Magnitude.IsApproximatelyEqualTo(1); + + /// + /// Gets a value indicating whether the has all of its components set to zero, or not. + /// + public bool IsZero => this.All(c => FloatingNumber.AreApproximatelyEqual(c, 0)); + /// /// Gets the LaTeX-string representing this vector graphically. /// @@ -111,7 +162,13 @@ public string ToLaTeXString() @"\left( \begin{array}{c} " + this[0] + @" \\ " + this[1] + @" \\ " + this[2] + @" \\ " + this[3] + @" \end{array} \right)"; - public static Vector4 FromVector(Vector vector) + /// + /// Generates a from an object implementing the interface, if the dimension is correct. + /// + /// The to generate a from. + /// The generated . + /// The dimension of the given vector is invalid. It must be 4. + public static Vector4 FromVector(IVector vector) { if (vector.Dimension != 4) throw new ArgumentException("The dimension of the given vector is invalid. It must be 4."); @@ -149,15 +206,9 @@ public override string ToString() /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() == typeof (Vector4)) return this == (Vector4) obj; - var vector = obj as Vector; + var vector = obj as IVector; if (Dimension != vector?.Dimension) return false; return this == FromVector(vector); @@ -182,6 +233,29 @@ public override int GetHashCode() } } + public bool Equals(Vector4 other) + { + return this == other; + } + + public IEnumerator GetEnumerator() + { + for (uint i = 0; i < 4; i++) + { + yield return this[i]; + } + yield break; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (uint i = 0; i < 4; i++) + { + yield return this[i]; + } + yield break; + } + /// /// Implements the operator ==. /// @@ -192,16 +266,7 @@ public override int GetHashCode() /// public static bool operator ==(Vector4 left, Vector4 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 4; ++i) - { - if (!FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return left.SequenceEqual(right); } /// @@ -214,16 +279,7 @@ public override int GetHashCode() /// public static bool operator !=(Vector4 left, Vector4 right) { - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return ReferenceEquals(left, right); - - for (uint i = 0; i < 4; ++i) - { - if (FloatingNumber.AreApproximatelyEqual(left[i], right[i])) - return false; - } - - return true; + return !left.SequenceEqual(right); } } } \ No newline at end of file diff --git a/SharpMath/Geometry/VectorExtensions.cs b/SharpMath/Geometry/VectorExtensions.cs deleted file mode 100644 index 362bc98..0000000 --- a/SharpMath/Geometry/VectorExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Author: Dominic Beger (Trade/ProgTrade) 2016 - -using SharpMath.Geometry.Exceptions; - -namespace SharpMath.Geometry -{ - public static class VectorExtensions - { - /// - /// Negates the specified . - /// - /// The negated . - /// The dimensions of the vectors do not equal each other. - public static T Negate(this Vector vector) where T : Vector, new() - { - var resultVector = new T(); - if (vector.Dimension != resultVector.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - for (uint i = 0; i < vector.Dimension; ++i) - resultVector[i] = -vector[i]; - return resultVector; - } - - /// - /// Calculates the normalized of this . - /// - /// The normalized . - /// The dimensions of the vectors do not equal each other. - public static T Normalize(this Vector vector) where T : Vector, new() - { - var resultVector = new T(); - if (vector.Dimension != resultVector.Dimension) - throw new DimensionException("The dimensions of the vectors do not equal each other."); - - for (uint i = 0; i < vector.Dimension; ++i) - resultVector[i] = vector[i]/vector.Magnitude; - return resultVector; - } - } -} \ No newline at end of file diff --git a/SharpMath/SharpMath.csproj b/SharpMath/SharpMath.csproj index f7af15f..3a93e2f 100644 --- a/SharpMath/SharpMath.csproj +++ b/SharpMath/SharpMath.csproj @@ -34,6 +34,7 @@ + @@ -45,25 +46,33 @@ - + + + + + + + + + + - - + + - - + - +