mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +01:00
affine: Add GeoM.Invert, IsInvertible (#547)
This commit is contained in:
parent
efea65ee58
commit
c2872017ab
12
geom.go
12
geom.go
@ -83,6 +83,18 @@ func (g *GeoM) Translate(tx, ty float64) {
|
|||||||
g.impl = g.impl.Translate(tx, ty)
|
g.impl = g.impl.Translate(tx, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsInvertible returns a boolean value indicating
|
||||||
|
// whether the matrix g is invertible or not.
|
||||||
|
func (g *GeoM) IsInvertible() bool {
|
||||||
|
return g.impl.IsInvertible()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert inverts the matrix.
|
||||||
|
// If g is not invertible, Invert panics.
|
||||||
|
func (g *GeoM) Invert() {
|
||||||
|
g.impl = g.impl.Invert()
|
||||||
|
}
|
||||||
|
|
||||||
// Rotate rotates the matrix by theta.
|
// Rotate rotates the matrix by theta.
|
||||||
// The unit is radian.
|
// The unit is radian.
|
||||||
func (g *GeoM) Rotate(theta float64) {
|
func (g *GeoM) Rotate(theta float64) {
|
||||||
|
97
geom_test.go
97
geom_test.go
@ -15,6 +15,7 @@
|
|||||||
package ebiten_test
|
package ebiten_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -116,6 +117,16 @@ func TestGeoMConcatSelf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func geoMToString(g GeoM) string {
|
||||||
|
a := g.Element(0, 0)
|
||||||
|
b := g.Element(0, 1)
|
||||||
|
c := g.Element(1, 0)
|
||||||
|
d := g.Element(1, 1)
|
||||||
|
tx := g.Element(0, 2)
|
||||||
|
ty := g.Element(1, 2)
|
||||||
|
return fmt.Sprintf("{a: %f, b: %f, c: %f, d: %f, tx: %f, ty: %f}", a, b, c, d, tx, ty)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGeoMApply(t *testing.T) {
|
func TestGeoMApply(t *testing.T) {
|
||||||
trans := GeoM{}
|
trans := GeoM{}
|
||||||
trans.Translate(1, 2)
|
trans.Translate(1, 2)
|
||||||
@ -172,7 +183,91 @@ func TestGeoMApply(t *testing.T) {
|
|||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
rx, ry := c.GeoM.Apply(c.InX, c.InY)
|
rx, ry := c.GeoM.Apply(c.InX, c.InY)
|
||||||
if math.Abs(rx-c.OutX) > c.Delta || math.Abs(ry-c.OutY) > c.Delta {
|
if math.Abs(rx-c.OutX) > c.Delta || math.Abs(ry-c.OutY) > c.Delta {
|
||||||
t.Errorf("%v.Apply(%v, %v) = (%v, %v), want (%v, %v)", c.GeoM, c.InX, c.InY, rx, ry, c.OutX, c.OutY)
|
t.Errorf("%s.Apply(%f, %f) = (%f, %f), want (%f, %f)", geoMToString(c.GeoM), c.InX, c.InY, rx, ry, c.OutX, c.OutY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeoMIsInvert(t *testing.T) {
|
||||||
|
zero := GeoM{}
|
||||||
|
zero.Scale(0, 0)
|
||||||
|
|
||||||
|
trans := GeoM{}
|
||||||
|
trans.Translate(1, 2)
|
||||||
|
|
||||||
|
scale := GeoM{}
|
||||||
|
scale.Scale(1.5, 2.5)
|
||||||
|
|
||||||
|
cpx := GeoM{}
|
||||||
|
cpx.Rotate(math.Pi)
|
||||||
|
cpx.Scale(1.5, 2.5)
|
||||||
|
cpx.Translate(-2, -3)
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
GeoM GeoM
|
||||||
|
Invertible bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
GeoM: zero,
|
||||||
|
Invertible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GeoM: GeoM{},
|
||||||
|
Invertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GeoM: trans,
|
||||||
|
Invertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GeoM: scale,
|
||||||
|
Invertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GeoM: cpx,
|
||||||
|
Invertible: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pts := []struct {
|
||||||
|
X float64
|
||||||
|
Y float64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 1,
|
||||||
|
Y: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 3.14159,
|
||||||
|
Y: 2.81828,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: -1000,
|
||||||
|
Y: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const delta = 0.00001
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
if c.GeoM.IsInvertible() != c.Invertible {
|
||||||
|
t.Errorf("%s.IsInvertible(): got: %t, want: %t", geoMToString(c.GeoM), c.GeoM.IsInvertible(), c.Invertible)
|
||||||
|
}
|
||||||
|
if !c.GeoM.IsInvertible() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
invGeoM := c.GeoM
|
||||||
|
invGeoM.Invert()
|
||||||
|
for _, p := range pts {
|
||||||
|
x, y := p.X, p.Y
|
||||||
|
gotX, gotY := invGeoM.Apply(c.GeoM.Apply(x, y))
|
||||||
|
if math.Abs(gotX-x) > delta || math.Abs(gotY-y) > delta {
|
||||||
|
t.Errorf("%s.Apply(%s.Apply(%f, %f)): got: (%f, %f), want: (%f, %f)", geoMToString(invGeoM), geoMToString(c.GeoM), x, y, gotX, gotY, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,3 +183,32 @@ func (g *GeoM) Rotate(theta float64) *GeoM {
|
|||||||
ty: sin*g.tx + cos*g.ty,
|
ty: sin*g.tx + cos*g.ty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GeoM) det() float64 {
|
||||||
|
if g == nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return (g.a_1+1)*(g.d_1+1) - g.b - g.c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GeoM) IsInvertible() bool {
|
||||||
|
return g.det() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GeoM) Invert() *GeoM {
|
||||||
|
if g == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
det := g.det()
|
||||||
|
if det == 0 {
|
||||||
|
panic("affine: g is not invertible")
|
||||||
|
}
|
||||||
|
return &GeoM{
|
||||||
|
a_1: ((g.d_1 + 1) / det) - 1,
|
||||||
|
b: -g.b / det,
|
||||||
|
c: -g.c / det,
|
||||||
|
d_1: ((g.a_1 + 1) / det) - 1,
|
||||||
|
tx: (-(g.d_1+1)*g.tx + g.b*g.ty) / det,
|
||||||
|
ty: (g.c*g.tx + -(g.a_1+1)*g.ty) / det,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user