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 }