go.mobile/f32: add Scale, Translate and Rotate methods to Affine.
Also make Mat4 methods consistent with Affine's methods. LGTM=crawshaw R=crawshaw CC=golang-codereviews https://golang.org/cl/156000043
This commit is contained in:
Родитель
07f3aef267
Коммит
6d01e6fdd5
|
@ -18,6 +18,7 @@ func (m Affine) String() string {
|
|||
m[1][0], m[1][1], m[1][2])
|
||||
}
|
||||
|
||||
// Identity sets m to be the identity transform.
|
||||
func (m *Affine) Identity() {
|
||||
*m = Affine{
|
||||
{1, 0, 0},
|
||||
|
@ -25,6 +26,8 @@ func (m *Affine) Identity() {
|
|||
}
|
||||
}
|
||||
|
||||
// Eq reports whether each component of m is within epsilon of the same
|
||||
// component in n.
|
||||
func (m *Affine) Eq(n *Affine, epsilon float32) bool {
|
||||
for i := range m {
|
||||
for j := range m[i] {
|
||||
|
@ -37,15 +40,15 @@ func (m *Affine) Eq(n *Affine, epsilon float32) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Mul stores a × b in m.
|
||||
func (m *Affine) Mul(a, b *Affine) {
|
||||
// Mul sets m to be p × q.
|
||||
func (m *Affine) Mul(p, q *Affine) {
|
||||
// Store the result in local variables, in case m == a || m == b.
|
||||
m00 := a[0][0]*b[0][0] + a[0][1]*b[1][0]
|
||||
m01 := a[0][0]*b[0][1] + a[0][1]*b[1][1]
|
||||
m02 := a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]
|
||||
m10 := a[1][0]*b[0][0] + a[1][1]*b[1][0]
|
||||
m11 := a[1][0]*b[0][1] + a[1][1]*b[1][1]
|
||||
m12 := a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]
|
||||
m00 := p[0][0]*q[0][0] + p[0][1]*q[1][0]
|
||||
m01 := p[0][0]*q[0][1] + p[0][1]*q[1][1]
|
||||
m02 := p[0][0]*q[0][2] + p[0][1]*q[1][2] + p[0][2]
|
||||
m10 := p[1][0]*q[0][0] + p[1][1]*q[1][0]
|
||||
m11 := p[1][0]*q[0][1] + p[1][1]*q[1][1]
|
||||
m12 := p[1][0]*q[0][2] + p[1][1]*q[1][2] + p[1][2]
|
||||
m[0][0] = m00
|
||||
m[0][1] = m01
|
||||
m[0][2] = m02
|
||||
|
@ -53,3 +56,35 @@ func (m *Affine) Mul(a, b *Affine) {
|
|||
m[1][1] = m11
|
||||
m[1][2] = m12
|
||||
}
|
||||
|
||||
// Scale sets m to be a scale followed by p.
|
||||
// It is equivalent to m.Mul(p, &Affine{{x,0,0}, {0,y,0}}).
|
||||
func (m *Affine) Scale(p *Affine, x, y float32) {
|
||||
m[0][0] = p[0][0] * x
|
||||
m[0][1] = p[0][1] * y
|
||||
m[0][2] = p[0][2]
|
||||
m[1][0] = p[1][0] * x
|
||||
m[1][1] = p[1][1] * y
|
||||
m[1][2] = p[1][2]
|
||||
}
|
||||
|
||||
// Translate sets m to be a translation followed by p.
|
||||
// It is equivalent to m.Mul(p, &Affine{{1,0,x}, {0,1,y}}).
|
||||
func (m *Affine) Translate(p *Affine, x, y float32) {
|
||||
m[0][0] = p[0][0]
|
||||
m[0][1] = p[0][1]
|
||||
m[0][2] = p[0][0]*x + p[0][1]*y + p[0][2]
|
||||
m[1][0] = p[1][0]
|
||||
m[1][1] = p[1][1]
|
||||
m[1][2] = p[1][0]*x + p[1][1]*y + p[1][2]
|
||||
}
|
||||
|
||||
// Rotate sets m to a rotation in radians followed by p.
|
||||
// It is equivalent to m.Mul(p, affineRotation).
|
||||
func (m *Affine) Rotate(p *Affine, radians float32) {
|
||||
s, c := Sin(radians), Cos(radians)
|
||||
m.Mul(p, &Affine{
|
||||
{+c, +s, 0},
|
||||
{-s, +c, 0},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package f32
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var xyTests = []struct {
|
||||
x, y float32
|
||||
}{
|
||||
{0, 0},
|
||||
{1, 1},
|
||||
{2, 3},
|
||||
{6.5, 4.3},
|
||||
}
|
||||
|
||||
var a = Affine{
|
||||
{3, 4, 5},
|
||||
{6, 7, 8},
|
||||
}
|
||||
|
||||
func TestAffineScale(t *testing.T) {
|
||||
for _, test := range xyTests {
|
||||
want := a
|
||||
want.Mul(&want, &Affine{{test.x, 0, 0}, {0, test.y, 0}})
|
||||
got := a
|
||||
got.Scale(&got, test.x, test.y)
|
||||
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("(%.2f, %.2f): got %s want %s", test.x, test.y, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAffineTranslate(t *testing.T) {
|
||||
for _, test := range xyTests {
|
||||
want := a
|
||||
want.Mul(&want, &Affine{{1, 0, test.x}, {0, 1, test.y}})
|
||||
got := a
|
||||
got.Translate(&got, test.x, test.y)
|
||||
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("(%.2f, %.2f): got %s want %s", test.x, test.y, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAffineRotate(t *testing.T) {
|
||||
want := Affine{
|
||||
{-4.000, 3.000, 5.000},
|
||||
{-7.000, 6.000, 8.000},
|
||||
}
|
||||
got := a
|
||||
got.Rotate(&got, math.Pi/2)
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("rotate π: got %s want %s", got, want)
|
||||
}
|
||||
|
||||
want = a
|
||||
got = a
|
||||
got.Rotate(&got, 2*math.Pi)
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("rotate 2π: got %s want %s", got, want)
|
||||
}
|
||||
|
||||
got = a
|
||||
got.Rotate(&got, math.Pi)
|
||||
got.Rotate(&got, math.Pi)
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("rotate π then π: got %s want %s", got, want)
|
||||
}
|
||||
|
||||
got = a
|
||||
got.Rotate(&got, math.Pi/3)
|
||||
got.Rotate(&got, -math.Pi/3)
|
||||
if !got.Eq(&want, 0.01) {
|
||||
t.Errorf("rotate π/3 then -π/3: got %s want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAffineScaleTranslate(t *testing.T) {
|
||||
mulVec := func(m *Affine, v [2]float32) (mv [2]float32) {
|
||||
mv[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]
|
||||
mv[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]
|
||||
return mv
|
||||
}
|
||||
v := [2]float32{1, 10}
|
||||
|
||||
var sThenT Affine
|
||||
sThenT.Identity()
|
||||
sThenT.Scale(&sThenT, 13, 17)
|
||||
sThenT.Translate(&sThenT, 101, 151)
|
||||
wantSTT := [2]float32{
|
||||
13 * (101 + 1),
|
||||
17 * (151 + 10),
|
||||
}
|
||||
if got := mulVec(&sThenT, v); got != wantSTT {
|
||||
t.Errorf("S then T: got %v, want %v", got, wantSTT)
|
||||
}
|
||||
|
||||
var tThenS Affine
|
||||
tThenS.Identity()
|
||||
tThenS.Translate(&tThenS, 101, 151)
|
||||
tThenS.Scale(&tThenS, 13, 17)
|
||||
wantTTS := [2]float32{
|
||||
101 + (13 * 1),
|
||||
151 + (17 * 10),
|
||||
}
|
||||
if got := mulVec(&tThenS, v); got != wantTTS {
|
||||
t.Errorf("T then S: got %v, want %v", got, wantTTS)
|
||||
}
|
||||
}
|
14
f32/f32.go
14
f32/f32.go
|
@ -4,13 +4,23 @@
|
|||
|
||||
// Package f32 implements some linear algebra and GL helpers for float32s.
|
||||
//
|
||||
// Types defined in this package have methods implementing common
|
||||
// mathematical operations. The common form for these functions is
|
||||
//
|
||||
// func (dst *T) Op(lhs, rhs *T)
|
||||
//
|
||||
// which reads in traditional mathematical notation as
|
||||
//
|
||||
// dst = lhs op rhs.
|
||||
//
|
||||
// It is safe to use the destination address as the left-hand side,
|
||||
// that is, dst *= rhs is dst.Mul(dst, rhs).
|
||||
//
|
||||
// WARNING
|
||||
//
|
||||
// The interface to this package is not stable. It will change considerably.
|
||||
// Only use functions that provide package documentation. Semantics are
|
||||
// non-obvious. Be prepared for the package name to change.
|
||||
//
|
||||
// Oh, and it's slow. Sorry.
|
||||
package f32
|
||||
|
||||
import "math"
|
||||
|
|
|
@ -268,14 +268,14 @@ func TestMat4Rotate(t *testing.T) {
|
|||
|
||||
func TestMat4Scale(t *testing.T) {
|
||||
want := &Mat4{
|
||||
{0.000, 2.000, 4.000, 6.000},
|
||||
{12.000, 15.000, 18.000, 21.000},
|
||||
{32.000, 36.000, 40.000, 44.000},
|
||||
{12.000, 13.000, 14.000, 15.000},
|
||||
{0 * 2, 1 * 3, 2 * 4, 3 * 1},
|
||||
{4 * 2, 5 * 3, 6 * 4, 7 * 1},
|
||||
{8 * 2, 9 * 3, 10 * 4, 11 * 1},
|
||||
{12 * 2, 13 * 3, 14 * 4, 15 * 1},
|
||||
}
|
||||
|
||||
got := new(Mat4)
|
||||
got.Scale(&x4, &Vec3{2, 3, 4})
|
||||
got.Scale(&x4, 2, 3, 4)
|
||||
|
||||
if !got.Eq(want, 0.01) {
|
||||
t.Errorf("got\n%s\nwant\n%s", got, want)
|
||||
|
@ -284,14 +284,14 @@ func TestMat4Scale(t *testing.T) {
|
|||
|
||||
func TestMat4Translate(t *testing.T) {
|
||||
want := &Mat4{
|
||||
{0.000, 1.000, 2.000, 3.000},
|
||||
{4.000, 5.000, 6.000, 7.000},
|
||||
{8.000, 9.000, 10.000, 11.000},
|
||||
{15.200, 16.800, 18.400, 20.000},
|
||||
{0, 1, 2, 0*0.1 + 1*0.2 + 2*0.3 + 3*1},
|
||||
{4, 5, 6, 4*0.1 + 5*0.2 + 6*0.3 + 7*1},
|
||||
{8, 9, 10, 8*0.1 + 9*0.2 + 10*0.3 + 11*1},
|
||||
{12, 13, 14, 12*0.1 + 13*0.2 + 14*0.3 + 15*1},
|
||||
}
|
||||
|
||||
got := new(Mat4)
|
||||
got.Translate(&x4, &Vec3{0.1, 0.2, 0.3})
|
||||
got.Translate(&x4, 0.1, 0.2, 0.3)
|
||||
|
||||
if !got.Eq(want, 0.01) {
|
||||
t.Errorf("got\n%s\nwant\n%s", got, want)
|
||||
|
|
72
f32/mat4.go
72
f32/mat4.go
|
@ -79,6 +79,7 @@ func (m *Mat4) Mul(a, b *Mat4) {
|
|||
m[3][3] = m33
|
||||
}
|
||||
|
||||
// Perspective sets m to be the GL perspective matrix.
|
||||
func (m *Mat4) Perspective(fov Radian, aspect, near, far float32) {
|
||||
t := Tan(float32(fov) / 2)
|
||||
|
||||
|
@ -89,33 +90,70 @@ func (m *Mat4) Perspective(fov Radian, aspect, near, far float32) {
|
|||
m[3][2] = -2 * far * near / (far - near)
|
||||
}
|
||||
|
||||
func (m *Mat4) Scale(src *Mat4, scale *Vec3) {
|
||||
for i, s := range scale {
|
||||
m[i][0] = src[i][0] * s
|
||||
m[i][1] = src[i][1] * s
|
||||
m[i][2] = src[i][2] * s
|
||||
m[i][3] = src[i][3] * s
|
||||
}
|
||||
m[3] = src[3]
|
||||
// Scale sets m to be a scale followed by p.
|
||||
// It is equivalent to
|
||||
// m.Mul(p, &Mat4{
|
||||
// {x, 0, 0, 0},
|
||||
// {0, y, 0, 0},
|
||||
// {0, 0, z, 0},
|
||||
// {0, 0, 0, 1},
|
||||
// }).
|
||||
func (m *Mat4) Scale(p *Mat4, x, y, z float32) {
|
||||
m[0][0] = p[0][0] * x
|
||||
m[0][1] = p[0][1] * y
|
||||
m[0][2] = p[0][2] * z
|
||||
m[0][3] = p[0][3]
|
||||
m[1][0] = p[1][0] * x
|
||||
m[1][1] = p[1][1] * y
|
||||
m[1][2] = p[1][2] * z
|
||||
m[1][3] = p[1][3]
|
||||
m[2][0] = p[2][0] * x
|
||||
m[2][1] = p[2][1] * y
|
||||
m[2][2] = p[2][2] * z
|
||||
m[2][3] = p[2][3]
|
||||
m[3][0] = p[3][0] * x
|
||||
m[3][1] = p[3][1] * y
|
||||
m[3][2] = p[3][2] * z
|
||||
m[3][3] = p[3][3]
|
||||
}
|
||||
|
||||
func (m *Mat4) Translate(src *Mat4, v *Vec3) {
|
||||
*m = *src
|
||||
|
||||
m[3][0] = src[0][0]*v[0] + src[1][0]*v[1] + src[2][0]*v[2] + src[3][0]
|
||||
m[3][1] = src[0][1]*v[0] + src[1][1]*v[1] + src[2][1]*v[2] + src[3][1]
|
||||
m[3][2] = src[0][2]*v[0] + src[1][2]*v[1] + src[2][2]*v[2] + src[3][2]
|
||||
m[3][3] = src[0][3]*v[0] + src[1][3]*v[1] + src[2][3]*v[2] + src[3][3]
|
||||
// Translate sets m to be a translation followed by p.
|
||||
// It is equivalent to
|
||||
// m.Mul(p, &Mat4{
|
||||
// {1, 0, 0, x},
|
||||
// {0, 1, 0, y},
|
||||
// {0, 0, 1, z},
|
||||
// {0, 0, 0, 1},
|
||||
// }).
|
||||
func (m *Mat4) Translate(p *Mat4, x, y, z float32) {
|
||||
m[0][0] = p[0][0]
|
||||
m[0][1] = p[0][1]
|
||||
m[0][2] = p[0][2]
|
||||
m[0][3] = p[0][0]*x + p[0][1]*y + p[0][2]*z + p[0][3]
|
||||
m[1][0] = p[1][0]
|
||||
m[1][1] = p[1][1]
|
||||
m[1][2] = p[1][2]
|
||||
m[1][3] = p[1][0]*x + p[1][1]*y + p[1][2]*z + p[1][3]
|
||||
m[2][0] = p[2][0]
|
||||
m[2][1] = p[2][1]
|
||||
m[2][2] = p[2][2]
|
||||
m[2][3] = p[2][0]*x + p[2][1]*y + p[2][2]*z + p[2][3]
|
||||
m[3][0] = p[3][0]
|
||||
m[3][1] = p[3][1]
|
||||
m[3][2] = p[3][2]
|
||||
m[3][3] = p[3][0]*x + p[3][1]*y + p[3][2]*z + p[3][3]
|
||||
}
|
||||
|
||||
func (m *Mat4) Rotate(src *Mat4, angle Radian, axis *Vec3) {
|
||||
// Rotate sets m to a rotation in radians around a specified axis, followed by p.
|
||||
// It is equivalent to m.Mul(p, affineRotation).
|
||||
func (m *Mat4) Rotate(p *Mat4, angle Radian, axis *Vec3) {
|
||||
a := *axis
|
||||
a.Normalize()
|
||||
|
||||
c, s := Cos(float32(angle)), Sin(float32(angle))
|
||||
d := 1 - c
|
||||
|
||||
m.Mul(src, &Mat4{{
|
||||
m.Mul(p, &Mat4{{
|
||||
c + d*a[0]*a[1],
|
||||
0 + d*a[0]*a[1] + s*a[2],
|
||||
0 + d*a[0]*a[1] - s*a[1],
|
||||
|
|
Загрузка…
Ссылка в новой задаче