github.com/utopiagio/gio@v0.0.8/f32/affine_test.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package f32
     4  
     5  import (
     6  	"math"
     7  	"testing"
     8  )
     9  
    10  func eq(p1, p2 Point) bool {
    11  	tol := 1e-5
    12  	dx, dy := p2.X-p1.X, p2.Y-p1.Y
    13  	return math.Abs(math.Sqrt(float64(dx*dx+dy*dy))) < tol
    14  }
    15  
    16  func eqaff(x, y Affine2D) bool {
    17  	tol := 1e-5
    18  	return math.Abs(float64(x.a-y.a)) < tol &&
    19  		math.Abs(float64(x.b-y.b)) < tol &&
    20  		math.Abs(float64(x.c-y.c)) < tol &&
    21  		math.Abs(float64(x.d-y.d)) < tol &&
    22  		math.Abs(float64(x.e-y.e)) < tol &&
    23  		math.Abs(float64(x.f-y.f)) < tol
    24  }
    25  
    26  func TestTransformOffset(t *testing.T) {
    27  	p := Point{X: 1, Y: 2}
    28  	o := Point{X: 2, Y: -3}
    29  
    30  	r := Affine2D{}.Offset(o).Transform(p)
    31  	if !eq(r, Pt(3, -1)) {
    32  		t.Errorf("offset transformation mismatch: have %v, want {3 -1}", r)
    33  	}
    34  	i := Affine2D{}.Offset(o).Invert().Transform(r)
    35  	if !eq(i, p) {
    36  		t.Errorf("offset transformation inverse mismatch: have %v, want %v", i, p)
    37  	}
    38  }
    39  
    40  func TestString(t *testing.T) {
    41  	tests := []struct {
    42  		in  Affine2D
    43  		exp string
    44  	}{
    45  		{
    46  			in:  NewAffine2D(9, 11, 13, 17, 19, 23),
    47  			exp: "[[9 11 13] [17 19 23]]",
    48  		}, {
    49  			in:  NewAffine2D(29, 31, 37, 43, 47, 53),
    50  			exp: "[[29 31 37] [43 47 53]]",
    51  		}, {
    52  			in:  NewAffine2D(29.142342, 31.4123412, 37.53152, 43.51324213, 47.123412, 53.14312342),
    53  			exp: "[[29.1423 31.4123 37.5315] [43.5132 47.1234 53.1431]]",
    54  		},
    55  	}
    56  	for _, test := range tests {
    57  		if test.in.String() != test.exp {
    58  			t.Errorf("string mismatch: have %q, want %q", test.in.String(), test.exp)
    59  		}
    60  	}
    61  }
    62  
    63  func TestTransformScale(t *testing.T) {
    64  	p := Point{X: 1, Y: 2}
    65  	s := Point{X: -1, Y: 2}
    66  
    67  	r := Affine2D{}.Scale(Point{}, s).Transform(p)
    68  	if !eq(r, Pt(-1, 4)) {
    69  		t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r)
    70  	}
    71  	i := Affine2D{}.Scale(Point{}, s).Invert().Transform(r)
    72  	if !eq(i, p) {
    73  		t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p)
    74  	}
    75  }
    76  
    77  func TestTransformRotate(t *testing.T) {
    78  	p := Point{X: 1, Y: 0}
    79  	a := float32(math.Pi / 2)
    80  
    81  	r := Affine2D{}.Rotate(Point{}, a).Transform(p)
    82  	if !eq(r, Pt(0, 1)) {
    83  		t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r)
    84  	}
    85  	i := Affine2D{}.Rotate(Point{}, a).Invert().Transform(r)
    86  	if !eq(i, p) {
    87  		t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p)
    88  	}
    89  }
    90  
    91  func TestTransformShear(t *testing.T) {
    92  	p := Point{X: 1, Y: 1}
    93  
    94  	r := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Transform(p)
    95  	if !eq(r, Pt(2, 1)) {
    96  		t.Errorf("shear transformation mismatch: have %v, want {2 1}", r)
    97  	}
    98  	i := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
    99  	if !eq(i, p) {
   100  		t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p)
   101  	}
   102  }
   103  
   104  func TestTransformMultiply(t *testing.T) {
   105  	p := Point{X: 1, Y: 2}
   106  	o := Point{X: 2, Y: -3}
   107  	s := Point{X: -1, Y: 2}
   108  	a := float32(-math.Pi / 2)
   109  
   110  	r := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p)
   111  	if !eq(r, Pt(1, 3)) {
   112  		t.Errorf("complex transformation mismatch: have %v, want {1 3}", r)
   113  	}
   114  	i := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
   115  	if !eq(i, p) {
   116  		t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p)
   117  	}
   118  }
   119  
   120  func TestPrimes(t *testing.T) {
   121  	xa := NewAffine2D(9, 11, 13, 17, 19, 23)
   122  	xb := NewAffine2D(29, 31, 37, 43, 47, 53)
   123  
   124  	pa := Point{X: 2, Y: 3}
   125  	pb := Point{X: 5, Y: 7}
   126  
   127  	for _, test := range []struct {
   128  		x   Affine2D
   129  		p   Point
   130  		exp Point
   131  	}{
   132  		{x: xa, p: pa, exp: Pt(64, 114)},
   133  		{x: xa, p: pb, exp: Pt(135, 241)},
   134  		{x: xb, p: pa, exp: Pt(188, 280)},
   135  		{x: xb, p: pb, exp: Pt(399, 597)},
   136  	} {
   137  		got := test.x.Transform(test.p)
   138  		if !eq(got, test.exp) {
   139  			t.Errorf("%v.Transform(%v): have %v, want %v", test.x, test.p, got, test.exp)
   140  		}
   141  	}
   142  
   143  	for _, test := range []struct {
   144  		x   Affine2D
   145  		exp Affine2D
   146  	}{
   147  		{x: xa, exp: NewAffine2D(-1.1875, 0.6875, -0.375, 1.0625, -0.5625, -0.875)},
   148  		{x: xb, exp: NewAffine2D(1.5666667, -1.0333333, -3.2000008, -1.4333333, 1-0.03333336, 1.7999992)},
   149  	} {
   150  		got := test.x.Invert()
   151  		if !eqaff(got, test.exp) {
   152  			t.Errorf("%v.Invert(): have %v, want %v", test.x, got, test.exp)
   153  		}
   154  	}
   155  
   156  	got := xa.Mul(xb)
   157  	exp := NewAffine2D(734, 796, 929, 1310, 1420, 1659)
   158  	if !eqaff(got, exp) {
   159  		t.Errorf("%v.Mul(%v): have %v, want %v", xa, xb, got, exp)
   160  	}
   161  }
   162  
   163  func TestTransformScaleAround(t *testing.T) {
   164  	p := Pt(-1, -1)
   165  	target := Pt(-6, -13)
   166  	pt := Affine2D{}.Scale(Pt(4, 5), Pt(2, 3)).Transform(p)
   167  	if !eq(pt, target) {
   168  		t.Log(pt, "!=", target)
   169  		t.Error("Scale not as expected")
   170  	}
   171  }
   172  
   173  func TestTransformRotateAround(t *testing.T) {
   174  	p := Pt(-1, -1)
   175  	pt := Affine2D{}.Rotate(Pt(1, 1), -math.Pi/2).Transform(p)
   176  	target := Pt(-1, 3)
   177  	if !eq(pt, target) {
   178  		t.Log(pt, "!=", target)
   179  		t.Error("Rotate not as expected")
   180  	}
   181  }
   182  
   183  func TestMulOrder(t *testing.T) {
   184  	A := Affine2D{}.Offset(Pt(100, 100))
   185  	B := Affine2D{}.Scale(Point{}, Pt(2, 2))
   186  	_ = A
   187  	_ = B
   188  
   189  	T1 := Affine2D{}.Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2))
   190  	T2 := B.Mul(A)
   191  
   192  	if T1 != T2 {
   193  		t.Log(T1)
   194  		t.Log(T2)
   195  		t.Error("multiplication / transform order not as expected")
   196  	}
   197  }
   198  
   199  func BenchmarkTransformOffset(b *testing.B) {
   200  	p := Point{X: 1, Y: 2}
   201  	o := Point{X: 0.5, Y: 0.5}
   202  	aff := Affine2D{}.Offset(o)
   203  
   204  	for i := 0; i < b.N; i++ {
   205  		p = aff.Transform(p)
   206  	}
   207  	_ = p
   208  }
   209  
   210  func BenchmarkTransformScale(b *testing.B) {
   211  	p := Point{X: 1, Y: 2}
   212  	s := Point{X: 0.5, Y: 0.5}
   213  	aff := Affine2D{}.Scale(Point{}, s)
   214  	for i := 0; i < b.N; i++ {
   215  		p = aff.Transform(p)
   216  	}
   217  	_ = p
   218  }
   219  
   220  func BenchmarkTransformRotate(b *testing.B) {
   221  	p := Point{X: 1, Y: 2}
   222  	a := float32(math.Pi / 2)
   223  	aff := Affine2D{}.Rotate(Point{}, a)
   224  	for i := 0; i < b.N; i++ {
   225  		p = aff.Transform(p)
   226  	}
   227  	_ = p
   228  }
   229  
   230  func BenchmarkTransformTranslateMultiply(b *testing.B) {
   231  	a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
   232  	t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5})
   233  
   234  	for i := 0; i < b.N; i++ {
   235  		a = a.Mul(t)
   236  	}
   237  }
   238  
   239  func BenchmarkTransformScaleMultiply(b *testing.B) {
   240  	a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
   241  	t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5})
   242  
   243  	for i := 0; i < b.N; i++ {
   244  		a = a.Mul(t)
   245  	}
   246  }
   247  
   248  func BenchmarkTransformMultiply(b *testing.B) {
   249  	a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
   250  	t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7)
   251  
   252  	for i := 0; i < b.N; i++ {
   253  		a = a.Mul(t)
   254  	}
   255  }