From cabaee680e66651f2a9a6e72996e9f21d47c274f Mon Sep 17 00:00:00 2001 From: Jacek Olszak <jacekolszak@gmail.com> Date: Tue, 12 Feb 2019 14:57:03 +0100 Subject: [PATCH] #159 Fix unproject for rotated matrix --- geometry.go | 12 +++++------- geometry_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/geometry.go b/geometry.go index 72d148e..621a0b4 100644 --- a/geometry.go +++ b/geometry.go @@ -401,13 +401,11 @@ func (m Matrix) Project(u Vec) Vec { // Unproject does the inverse operation to Project. // -// It turns out that multiplying a vector by the inverse matrix of m can be nearly-accomplished by -// subtracting the translate part of the matrix and multplying by the inverse of the top-left 2x2 -// matrix, and the inverse of a 2x2 matrix is simple enough to just be inlined in the computation. -// // Time complexity is O(1). func (m Matrix) Unproject(u Vec) Vec { - d := (m[0] * m[3]) - (m[1] * m[2]) - u.X, u.Y = (u.X-m[4])/d, (u.Y-m[5])/d - return Vec{u.X*m[3] - u.Y*m[1], u.Y*m[0] - u.X*m[2]} + det := m[0]*m[3] - m[2]*m[1] + return Vec{ + m[3]/det*u.X - m[2]/det*u.Y + m[2]*m[5] - m[3]*m[4], + -m[1]/det*u.X + m[0]/det*u.Y + m[1]*m[4] - m[0]*m[5], + } } diff --git a/geometry_test.go b/geometry_test.go index e1c1a6f..f97b8cd 100644 --- a/geometry_test.go +++ b/geometry_test.go @@ -2,6 +2,8 @@ package pixel_test import ( "fmt" + "github.com/stretchr/testify/assert" + "math" "testing" "github.com/faiface/pixel" @@ -77,3 +79,30 @@ func TestResizeRect(t *testing.T) { }) } } + +func TestMatrix_Unproject(t *testing.T) { + t.Run("for rotated matrix", func(t *testing.T) { + matrix := pixel.IM.Rotated(pixel.ZV, math.Pi/2) + unprojected := matrix.Unproject(pixel.V(0, 1)) + assert.InDelta(t, unprojected.X, 1, 0.01) + assert.InDelta(t, unprojected.Y, 0, 0.01) + }) + t.Run("for moved matrix", func(t *testing.T) { + matrix := pixel.IM.Moved(pixel.V(5, 5)) + unprojected := matrix.Unproject(pixel.V(0, 0)) + assert.InDelta(t, unprojected.X, -5, 0.01) + assert.InDelta(t, unprojected.Y, -5, 0.01) + }) + t.Run("for scaled matrix", func(t *testing.T) { + matrix := pixel.IM.Scaled(pixel.ZV, 2) + unprojected := matrix.Unproject(pixel.V(4, 4)) + assert.InDelta(t, unprojected.X, 2, 0.01) + assert.InDelta(t, unprojected.Y, 2, 0.01) + }) + t.Run("for singular matrix", func(t *testing.T) { + matrix := pixel.Matrix{0, 0, 0, 0, 0, 0} + unprojected := matrix.Unproject(pixel.ZV) + assert.True(t, math.IsNaN(unprojected.X)) + assert.True(t, math.IsNaN(unprojected.Y)) + }) +} -- GitLab