github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/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 TestTransformScale(t *testing.T) { 41 p := Point{X: 1, Y: 2} 42 s := Point{X: -1, Y: 2} 43 44 r := Affine2D{}.Scale(Point{}, s).Transform(p) 45 if !eq(r, Pt(-1, 4)) { 46 t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r) 47 } 48 i := Affine2D{}.Scale(Point{}, s).Invert().Transform(r) 49 if !eq(i, p) { 50 t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p) 51 } 52 } 53 54 func TestTransformRotate(t *testing.T) { 55 p := Point{X: 1, Y: 0} 56 a := float32(math.Pi / 2) 57 58 r := Affine2D{}.Rotate(Point{}, a).Transform(p) 59 if !eq(r, Pt(0, 1)) { 60 t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r) 61 } 62 i := Affine2D{}.Rotate(Point{}, a).Invert().Transform(r) 63 if !eq(i, p) { 64 t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p) 65 } 66 } 67 68 func TestTransformShear(t *testing.T) { 69 p := Point{X: 1, Y: 1} 70 71 r := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Transform(p) 72 if !eq(r, Pt(2, 1)) { 73 t.Errorf("shear transformation mismatch: have %v, want {2 1}", r) 74 } 75 i := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) 76 if !eq(i, p) { 77 t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p) 78 } 79 } 80 81 func TestTransformMultiply(t *testing.T) { 82 p := Point{X: 1, Y: 2} 83 o := Point{X: 2, Y: -3} 84 s := Point{X: -1, Y: 2} 85 a := float32(-math.Pi / 2) 86 87 r := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p) 88 if !eq(r, Pt(1, 3)) { 89 t.Errorf("complex transformation mismatch: have %v, want {1 3}", r) 90 } 91 i := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) 92 if !eq(i, p) { 93 t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p) 94 } 95 } 96 97 func TestPrimes(t *testing.T) { 98 xa := NewAffine2D(9, 11, 13, 17, 19, 23) 99 xb := NewAffine2D(29, 31, 37, 43, 47, 53) 100 101 pa := Point{X: 2, Y: 3} 102 pb := Point{X: 5, Y: 7} 103 104 for _, test := range []struct { 105 x Affine2D 106 p Point 107 exp Point 108 }{ 109 {x: xa, p: pa, exp: Pt(64, 114)}, 110 {x: xa, p: pb, exp: Pt(135, 241)}, 111 {x: xb, p: pa, exp: Pt(188, 280)}, 112 {x: xb, p: pb, exp: Pt(399, 597)}, 113 } { 114 got := test.x.Transform(test.p) 115 if !eq(got, test.exp) { 116 t.Errorf("%v.Transform(%v): have %v, want %v", test.x, test.p, got, test.exp) 117 } 118 } 119 120 for _, test := range []struct { 121 x Affine2D 122 exp Affine2D 123 }{ 124 {x: xa, exp: NewAffine2D(-1.1875, 0.6875, -0.375, 1.0625, -0.5625, -0.875)}, 125 {x: xb, exp: NewAffine2D(1.5666667, -1.0333333, -3.2000008, -1.4333333, 1-0.03333336, 1.7999992)}, 126 } { 127 got := test.x.Invert() 128 if !eqaff(got, test.exp) { 129 t.Errorf("%v.Invert(): have %v, want %v", test.x, got, test.exp) 130 } 131 } 132 133 got := xa.Mul(xb) 134 exp := NewAffine2D(734, 796, 929, 1310, 1420, 1659) 135 if !eqaff(got, exp) { 136 t.Errorf("%v.Mul(%v): have %v, want %v", xa, xb, got, exp) 137 } 138 } 139 140 func TestTransformScaleAround(t *testing.T) { 141 p := Pt(-1, -1) 142 target := Pt(-6, -13) 143 pt := Affine2D{}.Scale(Pt(4, 5), Pt(2, 3)).Transform(p) 144 if !eq(pt, target) { 145 t.Log(pt, "!=", target) 146 t.Error("Scale not as expected") 147 } 148 } 149 150 func TestTransformRotateAround(t *testing.T) { 151 p := Pt(-1, -1) 152 pt := Affine2D{}.Rotate(Pt(1, 1), -math.Pi/2).Transform(p) 153 target := Pt(-1, 3) 154 if !eq(pt, target) { 155 t.Log(pt, "!=", target) 156 t.Error("Rotate not as expected") 157 } 158 } 159 160 func TestMulOrder(t *testing.T) { 161 A := Affine2D{}.Offset(Pt(100, 100)) 162 B := Affine2D{}.Scale(Point{}, Pt(2, 2)) 163 _ = A 164 _ = B 165 166 T1 := Affine2D{}.Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2)) 167 T2 := B.Mul(A) 168 169 if T1 != T2 { 170 t.Log(T1) 171 t.Log(T2) 172 t.Error("multiplication / transform order not as expected") 173 } 174 } 175 176 func BenchmarkTransformOffset(b *testing.B) { 177 p := Point{X: 1, Y: 2} 178 o := Point{X: 0.5, Y: 0.5} 179 aff := Affine2D{}.Offset(o) 180 181 for i := 0; i < b.N; i++ { 182 p = aff.Transform(p) 183 } 184 _ = p 185 } 186 187 func BenchmarkTransformScale(b *testing.B) { 188 p := Point{X: 1, Y: 2} 189 s := Point{X: 0.5, Y: 0.5} 190 aff := Affine2D{}.Scale(Point{}, s) 191 for i := 0; i < b.N; i++ { 192 p = aff.Transform(p) 193 } 194 _ = p 195 } 196 197 func BenchmarkTransformRotate(b *testing.B) { 198 p := Point{X: 1, Y: 2} 199 a := float32(math.Pi / 2) 200 aff := Affine2D{}.Rotate(Point{}, a) 201 for i := 0; i < b.N; i++ { 202 p = aff.Transform(p) 203 } 204 _ = p 205 } 206 207 func BenchmarkTransformTranslateMultiply(b *testing.B) { 208 a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) 209 t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}) 210 211 for i := 0; i < b.N; i++ { 212 a = a.Mul(t) 213 } 214 } 215 216 func BenchmarkTransformScaleMultiply(b *testing.B) { 217 a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) 218 t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5}) 219 220 for i := 0; i < b.N; i++ { 221 a = a.Mul(t) 222 } 223 } 224 225 func BenchmarkTransformMultiply(b *testing.B) { 226 a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) 227 t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7) 228 229 for i := 0; i < b.N; i++ { 230 a = a.Mul(t) 231 } 232 }