diff --git a/gglm/mat2.go b/gglm/mat2.go index 9c92176..af0ce32 100755 --- a/gglm/mat2.go +++ b/gglm/mat2.go @@ -84,7 +84,6 @@ func (m *Mat2) Eq(m2 *Mat2) bool { } func (m *Mat2) Transpose() *Mat2 { - m.Data = [2][2]float32{ {m.Data[0][0], m.Data[1][0]}, {m.Data[0][1], m.Data[1][1]}, @@ -93,6 +92,27 @@ func (m *Mat2) Transpose() *Mat2 { return m } +func (m *Mat2) Determinant() float32 { + return (m.Data[0][0] * m.Data[1][1]) - (m.Data[0][1] * m.Data[1][0]) +} + +// Invert inverts this matrix. +// +// Note that the inverse is not defined if the determinant is zero or extremely small. +// In the case the determinant is zero the matrix will (usually) get filled with infinities +func (m *Mat2) Invert() *Mat2 { + + // https://www.cuemath.com/algebra/inverse-of-2x2-matrix/ + inverseDet := 1 / m.Determinant() + + m.Data = [2][2]float32{ + {m.Data[1][1] * inverseDet, -m.Data[0][1] * inverseDet}, // Col0 + {-m.Data[1][0] * inverseDet, m.Data[0][0] * inverseDet}, // Col1 + } + + return m +} + // AddMat2 m3 = m1 + m2 func AddMat2(m1, m2 *Mat2) *Mat2 { return &Mat2{ diff --git a/gglm/mat2_test.go b/gglm/mat2_test.go index 0192d90..e953662 100755 --- a/gglm/mat2_test.go +++ b/gglm/mat2_test.go @@ -179,6 +179,56 @@ func TestTransposeMat2(t *testing.T) { } } +func TestDeterminantMat2(t *testing.T) { + + m := gglm.NewMat2Id() + ans := float32(1) + + if m.Determinant() != ans { + t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans) + } + + m = &gglm.Mat2{ + Data: [2][2]float32{ + {1, 8}, + {5, 31}, + }, + } + ans = -9 + + if m.Determinant() != ans { + t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans) + } +} + +func TestInverseMat2(t *testing.T) { + + m := gglm.NewMat2Id() + ans := gglm.NewMat2Id() + + if !m.Invert().Eq(ans) { + t.Errorf("Got: %v; Expected: %v", m.String(), ans.String()) + } + + m = &gglm.Mat2{ + Data: [2][2]float32{ + {1, 8}, + {5, 31}, + }, + } + + ans = &gglm.Mat2{ + Data: [2][2]float32{ + {-31 / 9.0, 8 / 9.0}, + {5 / 9.0, -1 / 9.0}, + }, + } + + if !m.Invert().Eq(ans) { + t.Errorf("Got: %v; Expected: %v", m.String(), ans.String()) + } +} + func BenchmarkMulMat2(b *testing.B) { m1 := gglm.NewMat2Id() diff --git a/gglm/mat3.go b/gglm/mat3.go index ad374f4..e81232c 100755 --- a/gglm/mat3.go +++ b/gglm/mat3.go @@ -145,6 +145,64 @@ func (m *Mat3) Transpose() *Mat3 { return m } +func (m *Mat3) Determinant() float32 { + x := m.Data[0][0] * (m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]) + y := m.Data[1][0] * (m.Data[2][1]*m.Data[0][2] - m.Data[0][1]*m.Data[2][2]) + z := m.Data[2][0] * (m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2]) + return x + y + z +} + +// Invert inverts this matrix. +// +// Note that the inverse is not defined if the determinant is zero or extremely small. +// In the case the determinant is zero the matrix will (usually) get filled with infinities +func (m *Mat3) Invert() *Mat3 { + + // https://www.cuemath.com/algebra/inverse-of-3x3-matrix/ + + // Manually inline the determinant function because go doesn't + x := m.Data[0][0] * (m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]) + y := m.Data[1][0] * (m.Data[2][1]*m.Data[0][2] - m.Data[0][1]*m.Data[2][2]) + z := m.Data[2][0] * (m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2]) + + inverseDet := 1 / (x + y + z) + + // Inverse[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDeterminant; + // Inverse[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDeterminant; + // Inverse[2][0] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDeterminant; + // Inverse[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDeterminant; + // Inverse[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDeterminant; + // Inverse[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDeterminant; + // Inverse[0][2] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDeterminant; + // Inverse[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDeterminant; + // Inverse[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDeterminant; + + m.Data = [3][3]float32{ + // Col0 + { + (m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]) * inverseDet, + -(m.Data[0][1]*m.Data[2][2] - m.Data[2][1]*m.Data[0][2]) * inverseDet, + (m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2]) * inverseDet, + }, + + // Col1 + { + -(m.Data[1][0]*m.Data[2][2] - m.Data[2][0]*m.Data[1][2]) * inverseDet, + (m.Data[0][0]*m.Data[2][2] - m.Data[2][0]*m.Data[0][2]) * inverseDet, + -(m.Data[0][0]*m.Data[1][2] - m.Data[1][0]*m.Data[0][2]) * inverseDet, + }, + + // Col2 + { + (m.Data[1][0]*m.Data[2][1] - m.Data[2][0]*m.Data[1][1]) * inverseDet, + -(m.Data[0][0]*m.Data[2][1] - m.Data[2][0]*m.Data[0][1]) * inverseDet, + (m.Data[0][0]*m.Data[1][1] - m.Data[1][0]*m.Data[0][1]) * inverseDet, + }, + } + + return m +} + // AddMat3 m3 = m1 + m2 func AddMat3(m1, m2 *Mat3) *Mat3 { return &Mat3{ diff --git a/gglm/mat3_test.go b/gglm/mat3_test.go index 873b65e..e363649 100755 --- a/gglm/mat3_test.go +++ b/gglm/mat3_test.go @@ -196,6 +196,59 @@ func TestTransposeMat3(t *testing.T) { } } +func TestDeterminantMat3(t *testing.T) { + + m := gglm.NewMat3Id() + ans := float32(1) + + if m.Determinant() != ans { + t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans) + } + + m = &gglm.Mat3{ + Data: [3][3]float32{ + {1, 8, 2}, + {5, 3, 5}, + {9, 6, 7}, + }, + } + ans = 77 + + if m.Determinant() != ans { + t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans) + } +} + +func TestInverseMat3(t *testing.T) { + + m := gglm.NewMat3Id() + ans := gglm.NewMat3Id() + + if !m.Invert().Eq(ans) { + t.Errorf("Got: %v; Expected: %v", m.String(), ans.String()) + } + + m = &gglm.Mat3{ + Data: [3][3]float32{ + {1, 8, 2}, + {5, 3, 5}, + {9, 6, 7}, + }, + } + + ans = &gglm.Mat3{ + Data: [3][3]float32{ + {-9 / 77.0, -4 / 7.0, 34 / 77.0}, + {10 / 77.0, -1 / 7.0, 5 / 77.0}, + {3 / 77.0, 6 / 7.0, -37 / 77.0}, + }, + } + + if !m.Invert().Eq(ans) { + t.Errorf("Got: %v; Expected: %v", m.String(), ans.String()) + } +} + func BenchmarkMulMat3(b *testing.B) { m1 := gglm.NewMat3Id()