larb 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -1
  3. data/README.md +63 -1
  4. data/ext/larb/color.c +449 -0
  5. data/ext/larb/color.h +35 -0
  6. data/ext/larb/extconf.rb +11 -0
  7. data/ext/larb/larb.c +27 -0
  8. data/ext/larb/larb.h +31 -0
  9. data/ext/larb/mat2.c +303 -0
  10. data/ext/larb/mat2.h +30 -0
  11. data/ext/larb/mat2d.c +397 -0
  12. data/ext/larb/mat2d.h +35 -0
  13. data/ext/larb/mat3.c +455 -0
  14. data/ext/larb/mat3.h +33 -0
  15. data/ext/larb/mat4.c +651 -0
  16. data/ext/larb/mat4.h +31 -0
  17. data/ext/larb/matrix_utils.h +97 -0
  18. data/ext/larb/quat.c +567 -0
  19. data/ext/larb/quat.h +39 -0
  20. data/ext/larb/quat2.c +511 -0
  21. data/ext/larb/quat2.h +39 -0
  22. data/ext/larb/vec2.c +365 -0
  23. data/ext/larb/vec2.h +43 -0
  24. data/ext/larb/vec3.c +538 -0
  25. data/ext/larb/vec3.h +52 -0
  26. data/ext/larb/vec4.c +361 -0
  27. data/ext/larb/vec4.h +38 -0
  28. data/lib/larb/version.rb +5 -0
  29. data/lib/larb.rb +2 -14
  30. data/test/larb/color_test.rb +299 -0
  31. data/test/larb/mat2_test.rb +144 -0
  32. data/test/larb/mat2d_test.rb +197 -0
  33. data/test/larb/mat3_test.rb +147 -0
  34. data/test/larb/mat4_test.rb +347 -0
  35. data/test/larb/matrix_test.rb +135 -0
  36. data/test/larb/native_value_test.rb +157 -0
  37. data/test/larb/quat2_test.rb +254 -0
  38. data/test/larb/quat_test.rb +284 -0
  39. data/test/larb/vec2_test.rb +274 -0
  40. data/test/larb/vec3_test.rb +373 -0
  41. data/test/larb/vec4_test.rb +196 -0
  42. data/test/test_helper.rb +4 -0
  43. metadata +60 -18
  44. data/Rakefile +0 -11
  45. data/lib/larb/color.rb +0 -148
  46. data/lib/larb/mat2.rb +0 -119
  47. data/lib/larb/mat2d.rb +0 -180
  48. data/lib/larb/mat3.rb +0 -238
  49. data/lib/larb/mat4.rb +0 -329
  50. data/lib/larb/quat.rb +0 -238
  51. data/lib/larb/quat2.rb +0 -193
  52. data/lib/larb/vec2.rb +0 -150
  53. data/lib/larb/vec3.rb +0 -218
  54. data/lib/larb/vec4.rb +0 -125
@@ -0,0 +1,299 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+
5
+ class ColorTest < Test::Unit::TestCase
6
+ def test_bytes_clamp_before_integer_conversion
7
+ [1e8, 1e300, Float::INFINITY].each do |large|
8
+ assert_equal [255, 0, 128, 255], Larb::Color.new(large, -large, 0.5, 1).to_bytes
9
+ assert_equal "#ff0080ff", Larb::Color.new(large, -large, 0.5, 1).to_hex
10
+ end
11
+ 4.times do |i|
12
+ values = [0, 0, 0, 1]
13
+ values[i] = Float::NAN
14
+ assert_raise(ArgumentError) { Larb::Color.new(*values).to_bytes }
15
+ end
16
+ end
17
+
18
+ def test_from_hex_validates_the_entire_string
19
+ ["#gg0000", "#ff0000zz", "#ff0000ffjunk", "f#f0000", "", "#fff",
20
+ "#ffff", " ff0000", "#ff0000\n", "ff0000\0", "#ff0000"].each do |hex|
21
+ assert_raise(ArgumentError, hex.inspect) { Larb::Color.from_hex(hex) }
22
+ end
23
+ assert_raise(TypeError) { Larb::Color.from_hex(nil) }
24
+ assert_equal [255, 128, 0, 170], Larb::Color.from_hex("#FF8000aA").to_bytes
25
+ end
26
+
27
+ def test_new_with_default_values
28
+ c = Larb::Color.new
29
+ assert_equal 0.0, c.r
30
+ assert_equal 0.0, c.g
31
+ assert_equal 0.0, c.b
32
+ assert_equal 1.0, c.a
33
+ end
34
+
35
+ def test_new_with_given_values
36
+ c = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
37
+ assert_equal 0.1, c.r
38
+ assert_equal 0.2, c.g
39
+ assert_equal 0.3, c.b
40
+ assert_equal 0.4, c.a
41
+ end
42
+
43
+ def test_bracket_syntax
44
+ c = Larb::Color[0.1, 0.2, 0.3, 0.4]
45
+ assert_equal 0.1, c.r
46
+ assert_equal 0.2, c.g
47
+ assert_equal 0.3, c.b
48
+ assert_equal 0.4, c.a
49
+ end
50
+
51
+ def test_bracket_syntax_default_alpha
52
+ c = Larb::Color[0.1, 0.2, 0.3]
53
+ assert_equal 1.0, c.a
54
+ end
55
+
56
+ def test_rgb
57
+ c = Larb::Color.rgb(0.5, 0.6, 0.7)
58
+ assert_equal 0.5, c.r
59
+ assert_equal 0.6, c.g
60
+ assert_equal 0.7, c.b
61
+ assert_equal 1.0, c.a
62
+ end
63
+
64
+ def test_rgba
65
+ c = Larb::Color.rgba(0.1, 0.2, 0.3, 0.4)
66
+ assert_equal 0.1, c.r
67
+ assert_equal 0.2, c.g
68
+ assert_equal 0.3, c.b
69
+ assert_equal 0.4, c.a
70
+ end
71
+
72
+ def test_from_bytes
73
+ c = Larb::Color.from_bytes(255, 128, 0, 255)
74
+ assert_equal 1.0, c.r
75
+ assert_in_delta 0.5, c.g, 0.01
76
+ assert_equal 0.0, c.b
77
+ assert_equal 1.0, c.a
78
+ end
79
+
80
+ def test_from_bytes_default_alpha
81
+ c = Larb::Color.from_bytes(255, 0, 0)
82
+ assert_equal 1.0, c.a
83
+ end
84
+
85
+ def test_from_hex
86
+ c = Larb::Color.from_hex("#ff8000")
87
+ assert_equal 1.0, c.r
88
+ assert_in_delta 0.5, c.g, 0.01
89
+ assert_equal 0.0, c.b
90
+ assert_equal 1.0, c.a
91
+ end
92
+
93
+ def test_from_hex_without_hash
94
+ c = Larb::Color.from_hex("ff0000")
95
+ assert_equal 1.0, c.r
96
+ assert_equal 0.0, c.g
97
+ assert_equal 0.0, c.b
98
+ end
99
+
100
+ def test_from_hex_with_alpha
101
+ c = Larb::Color.from_hex("#ff000080")
102
+ assert_equal 1.0, c.r
103
+ assert_in_delta 0.5, c.a, 0.01
104
+ end
105
+
106
+ def test_black
107
+ c = Larb::Color.black
108
+ assert_equal 0.0, c.r
109
+ assert_equal 0.0, c.g
110
+ assert_equal 0.0, c.b
111
+ assert_equal 1.0, c.a
112
+ end
113
+
114
+ def test_white
115
+ c = Larb::Color.white
116
+ assert_equal 1.0, c.r
117
+ assert_equal 1.0, c.g
118
+ assert_equal 1.0, c.b
119
+ end
120
+
121
+ def test_red
122
+ c = Larb::Color.red
123
+ assert_equal 1.0, c.r
124
+ assert_equal 0.0, c.g
125
+ assert_equal 0.0, c.b
126
+ end
127
+
128
+ def test_green
129
+ c = Larb::Color.green
130
+ assert_equal 0.0, c.r
131
+ assert_equal 1.0, c.g
132
+ assert_equal 0.0, c.b
133
+ end
134
+
135
+ def test_blue
136
+ c = Larb::Color.blue
137
+ assert_equal 0.0, c.r
138
+ assert_equal 0.0, c.g
139
+ assert_equal 1.0, c.b
140
+ end
141
+
142
+ def test_yellow
143
+ c = Larb::Color.yellow
144
+ assert_equal 1.0, c.r
145
+ assert_equal 1.0, c.g
146
+ assert_equal 0.0, c.b
147
+ end
148
+
149
+ def test_cyan
150
+ c = Larb::Color.cyan
151
+ assert_equal 0.0, c.r
152
+ assert_equal 1.0, c.g
153
+ assert_equal 1.0, c.b
154
+ end
155
+
156
+ def test_magenta
157
+ c = Larb::Color.magenta
158
+ assert_equal 1.0, c.r
159
+ assert_equal 0.0, c.g
160
+ assert_equal 1.0, c.b
161
+ end
162
+
163
+ def test_transparent
164
+ c = Larb::Color.transparent
165
+ assert_equal 0.0, c.r
166
+ assert_equal 0.0, c.g
167
+ assert_equal 0.0, c.b
168
+ assert_equal 0.0, c.a
169
+ end
170
+
171
+ def test_add
172
+ c1 = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
173
+ c2 = Larb::Color.new(0.2, 0.3, 0.4, 0.5)
174
+ result = c1 + c2
175
+ assert_in_delta 0.3, result.r, 1e-10
176
+ assert_in_delta 0.5, result.g, 1e-10
177
+ assert_in_delta 0.7, result.b, 1e-10
178
+ assert_in_delta 0.9, result.a, 1e-10
179
+ end
180
+
181
+ def test_subtract
182
+ c1 = Larb::Color.new(0.5, 0.6, 0.7, 0.8)
183
+ c2 = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
184
+ result = c1 - c2
185
+ assert_in_delta 0.4, result.r, 1e-10
186
+ assert_in_delta 0.4, result.g, 1e-10
187
+ assert_in_delta 0.4, result.b, 1e-10
188
+ assert_in_delta 0.4, result.a, 1e-10
189
+ end
190
+
191
+ def test_multiply_by_scalar
192
+ c = Larb::Color.new(0.2, 0.3, 0.4, 0.5)
193
+ result = c * 2
194
+ assert_in_delta 0.4, result.r, 1e-10
195
+ assert_in_delta 0.6, result.g, 1e-10
196
+ assert_in_delta 0.8, result.b, 1e-10
197
+ assert_in_delta 1.0, result.a, 1e-10
198
+ end
199
+
200
+ def test_multiply_by_color
201
+ c1 = Larb::Color.new(0.5, 0.5, 0.5, 1.0)
202
+ c2 = Larb::Color.new(1.0, 0.5, 0.0, 1.0)
203
+ result = c1 * c2
204
+ assert_in_delta 0.5, result.r, 1e-10
205
+ assert_in_delta 0.25, result.g, 1e-10
206
+ assert_equal 0.0, result.b
207
+ end
208
+
209
+ def test_lerp
210
+ c1 = Larb::Color.new(0, 0, 0, 0)
211
+ c2 = Larb::Color.new(1, 1, 1, 1)
212
+ result = c1.lerp(c2, 0.5)
213
+ assert_equal 0.5, result.r
214
+ assert_equal 0.5, result.g
215
+ assert_equal 0.5, result.b
216
+ assert_equal 0.5, result.a
217
+ end
218
+
219
+ def test_clamp
220
+ c = Larb::Color.new(-0.5, 1.5, 0.5, 2.0)
221
+ result = c.clamp
222
+ assert_equal 0.0, result.r
223
+ assert_equal 1.0, result.g
224
+ assert_equal 0.5, result.b
225
+ assert_equal 1.0, result.a
226
+ end
227
+
228
+ def test_to_bytes
229
+ c = Larb::Color.new(1.0, 0.5, 0.0, 1.0)
230
+ bytes = c.to_bytes
231
+ assert_equal 255, bytes[0]
232
+ assert_equal 128, bytes[1]
233
+ assert_equal 0, bytes[2]
234
+ assert_equal 255, bytes[3]
235
+ end
236
+
237
+ def test_to_hex
238
+ c = Larb::Color.new(1.0, 0.0, 0.0, 1.0)
239
+ assert_equal "#ff0000ff", c.to_hex
240
+ end
241
+
242
+ def test_to_vec3
243
+ c = Larb::Color.new(0.1, 0.2, 0.3, 1.0)
244
+ v = c.to_vec3
245
+ assert_instance_of Larb::Vec3, v
246
+ assert_equal 0.1, v.x
247
+ assert_equal 0.2, v.y
248
+ assert_equal 0.3, v.z
249
+ end
250
+
251
+ def test_to_vec4
252
+ c = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
253
+ v = c.to_vec4
254
+ assert_instance_of Larb::Vec4, v
255
+ assert_equal 0.1, v.x
256
+ assert_equal 0.2, v.y
257
+ assert_equal 0.3, v.z
258
+ assert_equal 0.4, v.w
259
+ end
260
+
261
+ def test_to_a
262
+ c = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
263
+ assert_equal [0.1, 0.2, 0.3, 0.4], c.to_a
264
+ end
265
+
266
+ def test_from_vec4
267
+ v = Larb::Vec4.new(0.1, 0.2, 0.3, 0.4)
268
+ c = Larb::Color.from_vec4(v)
269
+ assert_equal 0.1, c.r
270
+ assert_equal 0.2, c.g
271
+ assert_equal 0.3, c.b
272
+ assert_equal 0.4, c.a
273
+ end
274
+
275
+ def test_from_vec3
276
+ v = Larb::Vec3.new(0.1, 0.2, 0.3)
277
+ c = Larb::Color.from_vec3(v)
278
+ assert_equal 0.1, c.r
279
+ assert_equal 0.2, c.g
280
+ assert_equal 0.3, c.b
281
+ assert_equal 1.0, c.a
282
+ end
283
+
284
+ def test_from_vec3_with_alpha
285
+ v = Larb::Vec3.new(0.1, 0.2, 0.3)
286
+ c = Larb::Color.from_vec3(v, 0.5)
287
+ assert_equal 0.5, c.a
288
+ end
289
+
290
+ def test_inspect
291
+ c = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
292
+ assert_equal "Color[0.1, 0.2, 0.3, 0.4]", c.inspect
293
+ end
294
+
295
+ def test_to_s
296
+ c = Larb::Color.new(0.1, 0.2, 0.3, 0.4)
297
+ assert_equal c.inspect, c.to_s
298
+ end
299
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+
5
+ class Mat2Test < Test::Unit::TestCase
6
+ def test_new_identity_by_default
7
+ m = Larb::Mat2.new
8
+ assert_equal 1.0, m[0]
9
+ assert_equal 0.0, m[1]
10
+ assert_equal 0.0, m[2]
11
+ assert_equal 1.0, m[3]
12
+ end
13
+
14
+ def test_new_with_data
15
+ m = Larb::Mat2.new([1, 2, 3, 4])
16
+ assert_equal [1, 2, 3, 4], m.data
17
+ end
18
+
19
+ def test_identity
20
+ m = Larb::Mat2.identity
21
+ assert_equal 1.0, m[0]
22
+ assert_equal 1.0, m[3]
23
+ end
24
+
25
+ def test_zero
26
+ m = Larb::Mat2.zero
27
+ 4.times { |i| assert_equal 0.0, m[i] }
28
+ end
29
+
30
+ def test_rotation
31
+ m = Larb::Mat2.rotation(Math::PI / 2)
32
+ assert_in_delta 0.0, m[0], 1e-10
33
+ assert_in_delta 1.0, m[1], 1e-10
34
+ assert_in_delta(-1.0, m[2], 1e-10)
35
+ assert_in_delta 0.0, m[3], 1e-10
36
+ end
37
+
38
+ def test_scaling
39
+ m = Larb::Mat2.scaling(2, 3)
40
+ assert_equal 2.0, m[0]
41
+ assert_equal 3.0, m[3]
42
+ end
43
+
44
+ def test_from_vec2
45
+ v1 = Larb::Vec2.new(1, 2)
46
+ v2 = Larb::Vec2.new(3, 4)
47
+ m = Larb::Mat2.from_vec2(v1, v2)
48
+ assert_equal [1.0, 2.0, 3.0, 4.0], m.data
49
+ end
50
+
51
+ def test_multiply_mat2
52
+ m1 = Larb::Mat2.rotation(Math::PI / 4)
53
+ m2 = Larb::Mat2.rotation(Math::PI / 4)
54
+ result = m1 * m2
55
+ expected = Larb::Mat2.rotation(Math::PI / 2)
56
+ assert result.near?(expected)
57
+ end
58
+
59
+ def test_multiply_vec2
60
+ m = Larb::Mat2.rotation(Math::PI / 2)
61
+ v = Larb::Vec2.new(1, 0)
62
+ result = m * v
63
+ assert_in_delta 0.0, result.x, 1e-10
64
+ assert_in_delta 1.0, result.y, 1e-10
65
+ end
66
+
67
+ def test_multiply_scalar
68
+ m = Larb::Mat2.identity
69
+ result = m * 2
70
+ assert_equal 2.0, result[0]
71
+ assert_equal 2.0, result[3]
72
+ end
73
+
74
+ def test_add
75
+ m1 = Larb::Mat2.identity
76
+ m2 = Larb::Mat2.identity
77
+ result = m1 + m2
78
+ assert_equal 2.0, result[0]
79
+ end
80
+
81
+ def test_subtract
82
+ m1 = Larb::Mat2.identity
83
+ m2 = Larb::Mat2.identity
84
+ result = m1 - m2
85
+ assert_equal 0.0, result[0]
86
+ end
87
+
88
+ def test_determinant
89
+ m = Larb::Mat2.identity
90
+ assert_equal 1.0, m.determinant
91
+ end
92
+
93
+ def test_determinant_scaling
94
+ m = Larb::Mat2.scaling(2, 3)
95
+ assert_equal 6.0, m.determinant
96
+ end
97
+
98
+ def test_inverse
99
+ m = Larb::Mat2.scaling(2, 4)
100
+ inv = m.inverse
101
+ result = m * inv
102
+ assert result.near?(Larb::Mat2.identity)
103
+ end
104
+
105
+ def test_inverse_singular
106
+ m = Larb::Mat2.zero
107
+ assert_raise_message("Matrix is not invertible") { m.inverse }
108
+ end
109
+
110
+ def test_transpose
111
+ m = Larb::Mat2.new([1, 2, 3, 4])
112
+ result = m.transpose
113
+ assert_equal [1, 3, 2, 4], result.data
114
+ end
115
+
116
+ def test_adjoint
117
+ m = Larb::Mat2.new([1, 2, 3, 4])
118
+ adj = m.adjoint
119
+ assert_equal 4.0, adj[0]
120
+ assert_equal(-2.0, adj[1])
121
+ end
122
+
123
+ def test_frobenius_norm
124
+ m = Larb::Mat2.new([1, 2, 2, 0])
125
+ assert_equal 3.0, m.frobenius_norm
126
+ end
127
+
128
+ def test_equality
129
+ m1 = Larb::Mat2.identity
130
+ m2 = Larb::Mat2.identity
131
+ assert_equal m1, m2
132
+ end
133
+
134
+ def test_near
135
+ m1 = Larb::Mat2.identity
136
+ m2 = Larb::Mat2.new([1.0000001, 0, 0, 1.0000001])
137
+ assert m1.near?(m2)
138
+ end
139
+
140
+ def test_inspect
141
+ m = Larb::Mat2.identity
142
+ assert_match(/Mat2/, m.inspect)
143
+ end
144
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+
5
+ class Mat2dTest < Test::Unit::TestCase
6
+ def test_new_identity_by_default
7
+ m = Larb::Mat2d.new
8
+ assert_equal 1.0, m[0]
9
+ assert_equal 0.0, m[1]
10
+ assert_equal 0.0, m[2]
11
+ assert_equal 1.0, m[3]
12
+ assert_equal 0.0, m[4]
13
+ assert_equal 0.0, m[5]
14
+ end
15
+
16
+ def test_new_with_data
17
+ data = [1, 2, 3, 4, 5, 6]
18
+ m = Larb::Mat2d.new(data)
19
+ assert_equal [1, 2, 3, 4, 5, 6], m.data
20
+ end
21
+
22
+ def test_identity
23
+ m = Larb::Mat2d.identity
24
+ assert_equal 1.0, m[0]
25
+ assert_equal 1.0, m[3]
26
+ end
27
+
28
+ def test_zero
29
+ m = Larb::Mat2d.zero
30
+ 6.times { |i| assert_equal 0.0, m[i] }
31
+ end
32
+
33
+ def test_translation
34
+ m = Larb::Mat2d.translation(10, 20)
35
+ assert_equal 10.0, m[4]
36
+ assert_equal 20.0, m[5]
37
+ end
38
+
39
+ def test_rotation
40
+ m = Larb::Mat2d.rotation(Math::PI / 2)
41
+ assert_in_delta 0.0, m[0], 1e-10
42
+ assert_in_delta 1.0, m[1], 1e-10
43
+ end
44
+
45
+ def test_scaling
46
+ m = Larb::Mat2d.scaling(2, 3)
47
+ assert_equal 2.0, m[0]
48
+ assert_equal 3.0, m[3]
49
+ end
50
+
51
+ def test_from_rotation_translation_scale
52
+ m = Larb::Mat2d.from_rotation_translation_scale(
53
+ 0,
54
+ Larb::Vec2.new(10, 20),
55
+ Larb::Vec2.new(2, 3)
56
+ )
57
+ assert_equal 2.0, m[0]
58
+ assert_equal 3.0, m[3]
59
+ assert_equal 10.0, m[4]
60
+ assert_equal 20.0, m[5]
61
+ end
62
+
63
+ def test_multiply_mat2d
64
+ m1 = Larb::Mat2d.translation(10, 0)
65
+ m2 = Larb::Mat2d.translation(5, 0)
66
+ result = m1 * m2
67
+ assert_equal 15.0, result[4]
68
+ end
69
+
70
+ def test_multiply_vec2
71
+ m = Larb::Mat2d.translation(10, 20)
72
+ v = Larb::Vec2.new(0, 0)
73
+ result = m * v
74
+ assert_equal 10.0, result.x
75
+ assert_equal 20.0, result.y
76
+ end
77
+
78
+ def test_multiply_rotation_translation
79
+ m = Larb::Mat2d.rotation(Math::PI / 2) * Larb::Mat2d.translation(1, 0)
80
+ v = Larb::Vec2.new(0, 0)
81
+ result = m * v
82
+ assert_in_delta 0.0, result.x, 1e-10
83
+ assert_in_delta 1.0, result.y, 1e-10
84
+ end
85
+
86
+ def test_determinant
87
+ m = Larb::Mat2d.identity
88
+ assert_equal 1.0, m.determinant
89
+ end
90
+
91
+ def test_determinant_scaling
92
+ m = Larb::Mat2d.scaling(2, 3)
93
+ assert_equal 6.0, m.determinant
94
+ end
95
+
96
+ def test_inverse
97
+ m = Larb::Mat2d.translation(10, 20) * Larb::Mat2d.scaling(2, 2)
98
+ inv = m.inverse
99
+ result = m * inv
100
+ assert result.near?(Larb::Mat2d.identity)
101
+ end
102
+
103
+ def test_inverse_singular
104
+ m = Larb::Mat2d.zero
105
+ assert_raise_message("Matrix is not invertible") { m.inverse }
106
+ end
107
+
108
+ def test_translate
109
+ m = Larb::Mat2d.identity
110
+ result = m.translate(5, 10)
111
+ assert_equal 5.0, result[4]
112
+ assert_equal 10.0, result[5]
113
+ end
114
+
115
+ def test_rotate
116
+ m = Larb::Mat2d.identity
117
+ result = m.rotate(Math::PI / 2)
118
+ assert_in_delta 0.0, result[0], 1e-10
119
+ end
120
+
121
+ def test_scale
122
+ m = Larb::Mat2d.identity
123
+ result = m.scale(2, 3)
124
+ assert_equal 2.0, result[0]
125
+ assert_equal 3.0, result[3]
126
+ end
127
+
128
+ def test_extract_translation
129
+ m = Larb::Mat2d.translation(10, 20)
130
+ t = m.extract_translation
131
+ assert_equal 10.0, t.x
132
+ assert_equal 20.0, t.y
133
+ end
134
+
135
+ def test_extract_rotation
136
+ m = Larb::Mat2d.rotation(Math::PI / 4)
137
+ angle = m.extract_rotation
138
+ assert_in_delta Math::PI / 4, angle, 1e-10
139
+ end
140
+
141
+ def test_extract_scale
142
+ m = Larb::Mat2d.scaling(2, 3)
143
+ s = m.extract_scale
144
+ assert_in_delta 2.0, s.x, 1e-10
145
+ assert_in_delta 3.0, s.y, 1e-10
146
+ end
147
+
148
+ def test_to_mat3
149
+ m = Larb::Mat2d.translation(10, 20)
150
+ m3 = m.to_mat3
151
+ assert_instance_of Larb::Mat3, m3
152
+ assert_equal 10.0, m3[6]
153
+ assert_equal 20.0, m3[7]
154
+ end
155
+
156
+ def test_equality
157
+ m1 = Larb::Mat2d.identity
158
+ m2 = Larb::Mat2d.identity
159
+ assert_equal m1, m2
160
+ end
161
+
162
+ def test_near
163
+ m1 = Larb::Mat2d.identity
164
+ m2 = Larb::Mat2d.new([1.0000001, 0, 0, 1.0000001, 0, 0])
165
+ assert m1.near?(m2)
166
+ end
167
+
168
+ def test_inspect
169
+ m = Larb::Mat2d.identity
170
+ assert_match(/Mat2d/, m.inspect)
171
+ end
172
+
173
+ def test_decomposition_round_trips_signed_scales
174
+ [-1, 1].repeated_permutation(2) do |signs|
175
+ [0, Math::PI / 2, 1.2].each do |angle|
176
+ matrix = Larb::Mat2d.from_rotation_translation_scale(
177
+ angle, Larb::Vec2.new(1, 2), Larb::Vec2.new(signs[0] * 2, signs[1] * 3)
178
+ )
179
+ scale = matrix.extract_scale
180
+ assert_equal signs.inject(:*) < 0, scale.x < 0
181
+ rebuilt = Larb::Mat2d.from_rotation_translation_scale(
182
+ matrix.extract_rotation, matrix.extract_translation, scale
183
+ )
184
+ assert rebuilt.to_a.all?(&:finite?)
185
+ matrix.to_a.zip(rebuilt.to_a).each { |a, b| assert_in_delta a, b, 1e-9 }
186
+ end
187
+ end
188
+ end
189
+
190
+ def test_decomposition_rejects_shear_zero_scale_and_nonfinite_values
191
+ [Larb::Mat2d.new([1, 0, 0.5, 1, 0, 0]), Larb::Mat2d.scaling(0, 1),
192
+ Larb::Mat2d.scaling(Float::NAN, 1)].each do |matrix|
193
+ assert_raise(ArgumentError) { matrix.extract_scale }
194
+ assert_raise(ArgumentError) { matrix.extract_rotation }
195
+ end
196
+ end
197
+ end