#159 Fix unproject for rotated matrix

This commit is contained in:
Jacek Olszak 2019-02-12 14:57:03 +01:00
parent 20b05d9ec2
commit cabaee680e
2 changed files with 34 additions and 7 deletions

View File

@ -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],
}
}

View File

@ -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))
})
}