vector2d 2.2.4 → 3.0.0
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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +39 -0
- data/.github/workflows/build.yml +48 -10
- data/.gitignore +3 -1
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +5 -2
- data/.tool-versions +1 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +224 -0
- data/CODE_OF_CONDUCT.md +92 -0
- data/CONTRIBUTING.md +30 -0
- data/Gemfile +6 -2
- data/Gemfile.lock +85 -51
- data/README.md +330 -13
- data/Rakefile +21 -0
- data/benchmark/vector_comparison.rb +129 -0
- data/lib/vector2d/angles.rb +315 -0
- data/lib/vector2d/arithmetic.rb +97 -0
- data/lib/vector2d/comparison.rb +188 -0
- data/lib/vector2d/componentwise.rb +239 -0
- data/lib/vector2d/constructors.rb +137 -0
- data/lib/vector2d/conversions.rb +139 -0
- data/lib/vector2d/coordinates.rb +22 -0
- data/lib/vector2d/deprecation.rb +16 -0
- data/lib/vector2d/dimensions.rb +297 -0
- data/lib/vector2d/interpolation.rb +191 -0
- data/lib/vector2d/lengths.rb +284 -0
- data/lib/vector2d/matrix_interop.rb +93 -0
- data/lib/vector2d/parsing.rb +142 -0
- data/lib/vector2d/projection.rb +208 -0
- data/lib/vector2d/version.rb +2 -1
- data/lib/vector2d.rb +174 -62
- data/release-please-config.json +10 -0
- data/spec/lib/vector2d/angles_spec.rb +441 -0
- data/spec/lib/vector2d/{calculations_spec.rb → arithmetic_spec.rb} +48 -58
- data/spec/lib/vector2d/comparison_spec.rb +358 -0
- data/spec/lib/vector2d/componentwise_spec.rb +300 -0
- data/spec/lib/vector2d/constructors_spec.rb +299 -0
- data/spec/lib/vector2d/conversions_spec.rb +234 -0
- data/spec/lib/vector2d/dimensions_spec.rb +820 -0
- data/spec/lib/vector2d/interpolation_spec.rb +322 -0
- data/spec/lib/vector2d/lengths_spec.rb +457 -0
- data/spec/lib/vector2d/matrix_interop_spec.rb +92 -0
- data/spec/lib/vector2d/parsing_spec.rb +329 -0
- data/spec/lib/vector2d/projection_spec.rb +306 -0
- data/spec/lib/vector2d_documentation_spec.rb +38 -0
- data/spec/lib/vector2d_immutability_spec.rb +263 -0
- data/spec/lib/vector2d_spec.rb +90 -50
- data/spec/lib/vector2d_subclassing_spec.rb +210 -0
- data/spec/lib/vector2d_tags_spec.rb +43 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/doc_examples/comments.rb +65 -0
- data/spec/support/doc_examples/markdown.rb +50 -0
- data/spec/support/doc_examples.rb +94 -0
- data/spec/support/doc_tags.rb +151 -0
- data/spec/support/shared_examples/class_preserving_method.rb +10 -0
- data/spec/support/shared_examples/deprecated_method.rb +25 -0
- data/spec/support/shared_examples/parsed_vector.rb +11 -0
- data/vector2d.gemspec +9 -3
- metadata +55 -19
- data/.travis.yml +0 -10
- data/lib/vector2d/calculations.rb +0 -141
- data/lib/vector2d/coercions.rb +0 -64
- data/lib/vector2d/fitting.rb +0 -60
- data/lib/vector2d/properties.rb +0 -46
- data/lib/vector2d/transformations.rb +0 -85
- data/spec/lib/vector2d/coercions_spec.rb +0 -59
- data/spec/lib/vector2d/fitting_spec.rb +0 -70
- data/spec/lib/vector2d/properties_spec.rb +0 -47
- data/spec/lib/vector2d/transformations_spec.rb +0 -122
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Vector2d::Dimensions do
|
|
6
|
+
subject(:vector) { Vector2d.new(2, 3) }
|
|
7
|
+
|
|
8
|
+
let(:original) { Vector2d.new(300, 300) }
|
|
9
|
+
|
|
10
|
+
describe "#area" do
|
|
11
|
+
it "multiplies the coordinates" do
|
|
12
|
+
expect(vector.area).to eq(6)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "is never negative" do
|
|
16
|
+
expect(Vector2d.new(-2, 3).area).to eq(6)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "is zero without width or height" do
|
|
20
|
+
expect(Vector2d.new(2, 0).area).to eq(0)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#aspect_ratio" do
|
|
25
|
+
it "returns the aspect_ratio" do
|
|
26
|
+
expect(vector.aspect_ratio).to be_within(0.0001).of(0.6667)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "when y is zero" do
|
|
30
|
+
let(:vector) { Vector2d.new(2, 0) }
|
|
31
|
+
|
|
32
|
+
it "raises an ArgumentError" do
|
|
33
|
+
expect { vector.aspect_ratio }
|
|
34
|
+
.to raise_error(ArgumentError,
|
|
35
|
+
"Vector2d(2,0) has no aspect ratio, y is zero")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "with the zero vector" do
|
|
40
|
+
let(:vector) { Vector2d.new(0, 0) }
|
|
41
|
+
|
|
42
|
+
it "raises an ArgumentError" do
|
|
43
|
+
expect { vector.aspect_ratio }
|
|
44
|
+
.to raise_error(ArgumentError,
|
|
45
|
+
"the zero vector has no aspect ratio")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "#landscape?" do
|
|
51
|
+
subject { vector.landscape? }
|
|
52
|
+
|
|
53
|
+
context "when the vector is wider than it is tall" do
|
|
54
|
+
let(:vector) { Vector2d.new(3, 2) }
|
|
55
|
+
|
|
56
|
+
it { is_expected.to be(true) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "when the vector is taller than it is wide" do
|
|
60
|
+
let(:vector) { Vector2d.new(2, 3) }
|
|
61
|
+
|
|
62
|
+
it { is_expected.to be(false) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context "when the vector is square" do
|
|
66
|
+
let(:vector) { Vector2d.new(2, 2) }
|
|
67
|
+
|
|
68
|
+
it { is_expected.to be(false) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context "when the coordinates are negative" do
|
|
72
|
+
let(:vector) { Vector2d.new(-3, -2) }
|
|
73
|
+
|
|
74
|
+
it { is_expected.to be(true) }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe "#portrait?" do
|
|
79
|
+
subject { vector.portrait? }
|
|
80
|
+
|
|
81
|
+
context "when the vector is taller than it is wide" do
|
|
82
|
+
let(:vector) { Vector2d.new(2, 3) }
|
|
83
|
+
|
|
84
|
+
it { is_expected.to be(true) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context "when the vector is wider than it is tall" do
|
|
88
|
+
let(:vector) { Vector2d.new(3, 2) }
|
|
89
|
+
|
|
90
|
+
it { is_expected.to be(false) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
context "when the vector is square" do
|
|
94
|
+
let(:vector) { Vector2d.new(2, 2) }
|
|
95
|
+
|
|
96
|
+
it { is_expected.to be(false) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "when the coordinates are negative" do
|
|
100
|
+
let(:vector) { Vector2d.new(-2, -3) }
|
|
101
|
+
|
|
102
|
+
it { is_expected.to be(true) }
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe "#square?" do
|
|
107
|
+
subject { vector.square? }
|
|
108
|
+
|
|
109
|
+
context "when the coordinates are equal" do
|
|
110
|
+
let(:vector) { Vector2d.new(2, 2) }
|
|
111
|
+
|
|
112
|
+
it { is_expected.to be(true) }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context "when the coordinates differ" do
|
|
116
|
+
let(:vector) { Vector2d.new(2, 3) }
|
|
117
|
+
|
|
118
|
+
it { is_expected.to be(false) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
context "when the coordinates differ only in sign" do
|
|
122
|
+
let(:vector) { Vector2d.new(-2, 2) }
|
|
123
|
+
|
|
124
|
+
it { is_expected.to be(true) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "with the zero vector" do
|
|
128
|
+
let(:vector) { Vector2d.new(0, 0) }
|
|
129
|
+
|
|
130
|
+
it { is_expected.to be(true) }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe "the shape predicates" do
|
|
135
|
+
it "are exclusive" do
|
|
136
|
+
[Vector2d.new(3, 2), Vector2d.new(2, 3), Vector2d.new(2, 2),
|
|
137
|
+
Vector2d.new(0, 0)].each do |v|
|
|
138
|
+
expect([v.landscape?, v.portrait?, v.square?].count(true)).to eq(1)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe "#fit" do
|
|
144
|
+
subject(:vector) { original.fit(comp) }
|
|
145
|
+
|
|
146
|
+
context "when scaling by height" do
|
|
147
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
148
|
+
|
|
149
|
+
its(:x) { is_expected.to eq(150) }
|
|
150
|
+
its(:y) { is_expected.to eq(150) }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
context "when scaling by width" do
|
|
154
|
+
let(:comp) { Vector2d.new(150, 200) }
|
|
155
|
+
|
|
156
|
+
its(:x) { is_expected.to eq(150) }
|
|
157
|
+
its(:y) { is_expected.to eq(150) }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context "when the x coordinate is negative" do
|
|
161
|
+
let(:original) { Vector2d.new(-20, 10) }
|
|
162
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
163
|
+
|
|
164
|
+
its(:x) { is_expected.to eq(-5) }
|
|
165
|
+
its(:y) { is_expected.to eq(2.5) }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
context "when the y coordinate is negative" do
|
|
169
|
+
let(:original) { Vector2d.new(20, -10) }
|
|
170
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
171
|
+
|
|
172
|
+
its(:x) { is_expected.to eq(5) }
|
|
173
|
+
its(:y) { is_expected.to eq(-2.5) }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context "when both coordinates are negative" do
|
|
177
|
+
let(:original) { Vector2d.new(-20, -10) }
|
|
178
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
179
|
+
|
|
180
|
+
its(:x) { is_expected.to eq(-5) }
|
|
181
|
+
its(:y) { is_expected.to eq(-2.5) }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context "when both coordinates are negative and the y axis binds" do
|
|
185
|
+
let(:original) { Vector2d.new(-10, -20) }
|
|
186
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
187
|
+
|
|
188
|
+
its(:x) { is_expected.to eq(-2.5) }
|
|
189
|
+
its(:y) { is_expected.to eq(-5) }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context "when the constraint is negative" do
|
|
193
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
194
|
+
let(:comp) { Vector2d.new(-5, -5) }
|
|
195
|
+
|
|
196
|
+
its(:x) { is_expected.to eq(5) }
|
|
197
|
+
its(:y) { is_expected.to eq(2.5) }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
context "with the zero vector" do
|
|
201
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
202
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
203
|
+
|
|
204
|
+
it "returns the zero vector" do
|
|
205
|
+
expect(vector).to eq(Vector2d.new(0, 0))
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "when the argument is invalid" do
|
|
210
|
+
let(:comp) { "garbage" }
|
|
211
|
+
|
|
212
|
+
it "raises an error" do
|
|
213
|
+
expect { vector }.to raise_error(ArgumentError)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
context "when the vector is zero and the argument is invalid" do
|
|
218
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
219
|
+
let(:comp) { "garbage" }
|
|
220
|
+
|
|
221
|
+
it "raises an error" do
|
|
222
|
+
expect { vector }.to raise_error(ArgumentError)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
context "when the x axis is zero" do
|
|
227
|
+
let(:original) { Vector2d.new(0, 5) }
|
|
228
|
+
let(:comp) { Vector2d.new(10, 2) }
|
|
229
|
+
|
|
230
|
+
it "disregards the zero axis" do
|
|
231
|
+
expect(vector).to eq(Vector2d.new(0.0, 2.0))
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
context "when the y axis is zero" do
|
|
236
|
+
let(:original) { Vector2d.new(5, 0) }
|
|
237
|
+
let(:comp) { Vector2d.new(2, 10) }
|
|
238
|
+
|
|
239
|
+
it "disregards the zero axis" do
|
|
240
|
+
expect(vector).to eq(Vector2d.new(2.0, 0.0))
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
context "when both constraint axes are zero" do
|
|
245
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
246
|
+
let(:comp) { Vector2d.new(0, 0) }
|
|
247
|
+
|
|
248
|
+
it "returns the vector unchanged" do
|
|
249
|
+
expect(vector).to eq(Vector2d.new(20, 10))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
context "when upscaling is enabled" do
|
|
254
|
+
subject(:vector) { original.fit(comp, upscale: true) }
|
|
255
|
+
|
|
256
|
+
let(:comp) { Vector2d.new(600, 600) }
|
|
257
|
+
|
|
258
|
+
its(:x) { is_expected.to eq(600) }
|
|
259
|
+
its(:y) { is_expected.to eq(600) }
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
context "when upscaling is disabled and the vector is larger" do
|
|
263
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
264
|
+
|
|
265
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
266
|
+
|
|
267
|
+
its(:x) { is_expected.to eq(150) }
|
|
268
|
+
its(:y) { is_expected.to eq(150) }
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
context "when upscaling is disabled and the vector already fits" do
|
|
272
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
273
|
+
|
|
274
|
+
let(:comp) { Vector2d.new(600, 600) }
|
|
275
|
+
|
|
276
|
+
it { is_expected.to equal(original) }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
context "when upscaling is disabled and the vector fits exactly" do
|
|
280
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
281
|
+
|
|
282
|
+
let(:comp) { Vector2d.new(300, 300) }
|
|
283
|
+
|
|
284
|
+
it { is_expected.to equal(original) }
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
context "when upscaling is disabled and only one axis fits" do
|
|
288
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
289
|
+
|
|
290
|
+
let(:comp) { Vector2d.new(600, 150) }
|
|
291
|
+
|
|
292
|
+
its(:x) { is_expected.to eq(150) }
|
|
293
|
+
its(:y) { is_expected.to eq(150) }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
context "when upscaling is disabled and the vector is negative" do
|
|
297
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
298
|
+
|
|
299
|
+
let(:original) { Vector2d.new(-20, -10) }
|
|
300
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
301
|
+
|
|
302
|
+
its(:x) { is_expected.to eq(-5) }
|
|
303
|
+
its(:y) { is_expected.to eq(-2.5) }
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
context "when upscaling is disabled and the vector is unconstrained" do
|
|
307
|
+
subject(:vector) { original.fit(comp, upscale: false) }
|
|
308
|
+
|
|
309
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
310
|
+
let(:comp) { Vector2d.new(0, 0) }
|
|
311
|
+
|
|
312
|
+
it { is_expected.to equal(original) }
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
describe "#fits?" do
|
|
317
|
+
subject { original.fits?(comp) }
|
|
318
|
+
|
|
319
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
320
|
+
|
|
321
|
+
context "when the vector is smaller than the constraint" do
|
|
322
|
+
let(:comp) { Vector2d.new(40, 40) }
|
|
323
|
+
|
|
324
|
+
it { is_expected.to be(true) }
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
context "when the vector overflows the x axis" do
|
|
328
|
+
let(:comp) { Vector2d.new(10, 40) }
|
|
329
|
+
|
|
330
|
+
it { is_expected.to be(false) }
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
context "when the vector overflows the y axis" do
|
|
334
|
+
let(:comp) { Vector2d.new(40, 5) }
|
|
335
|
+
|
|
336
|
+
it { is_expected.to be(false) }
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
context "when the vector matches the constraint" do
|
|
340
|
+
let(:comp) { Vector2d.new(20, 10) }
|
|
341
|
+
|
|
342
|
+
it { is_expected.to be(true) }
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
context "when the coordinates are negative" do
|
|
346
|
+
let(:original) { Vector2d.new(-20, -10) }
|
|
347
|
+
let(:comp) { Vector2d.new(40, 40) }
|
|
348
|
+
|
|
349
|
+
it { is_expected.to be(true) }
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
context "when an axis of the constraint is zero" do
|
|
353
|
+
let(:comp) { Vector2d.new(0, 40) }
|
|
354
|
+
|
|
355
|
+
it { is_expected.to be(true) }
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
context "when the unconstrained axis is the overflowing one" do
|
|
359
|
+
let(:comp) { Vector2d.new(40, 0) }
|
|
360
|
+
let(:original) { Vector2d.new(20, 80) }
|
|
361
|
+
|
|
362
|
+
it { is_expected.to be(true) }
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
context "with the zero vector" do
|
|
366
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
367
|
+
let(:comp) { Vector2d.new(40, 40) }
|
|
368
|
+
|
|
369
|
+
it { is_expected.to be(true) }
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it "coerces the constraint" do
|
|
373
|
+
expect(original.fits?("40x40")).to be(true)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it "agrees with #fit" do
|
|
377
|
+
[Vector2d.new(40, 40), Vector2d.new(10, 40),
|
|
378
|
+
Vector2d.new(20, 10)].each do |c|
|
|
379
|
+
expect(original.fits?(c))
|
|
380
|
+
.to be(original.fit(c, upscale: false) == original)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
describe "#cover" do
|
|
386
|
+
subject(:vector) { original.cover(comp) }
|
|
387
|
+
|
|
388
|
+
context "when width is largest" do
|
|
389
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
390
|
+
|
|
391
|
+
its(:x) { is_expected.to eq(200) }
|
|
392
|
+
its(:y) { is_expected.to eq(200) }
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
context "when height is largest" do
|
|
396
|
+
let(:comp) { Vector2d.new(150, 200) }
|
|
397
|
+
|
|
398
|
+
its(:x) { is_expected.to eq(200) }
|
|
399
|
+
its(:y) { is_expected.to eq(200) }
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
context "when a coordinate is negative" do
|
|
403
|
+
let(:original) { Vector2d.new(-300, 300) }
|
|
404
|
+
let(:comp) { Vector2d.new(150, 150) }
|
|
405
|
+
|
|
406
|
+
its(:x) { is_expected.to eq(-150) }
|
|
407
|
+
its(:y) { is_expected.to eq(150) }
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
context "when a coordinate is negative and width is largest" do
|
|
411
|
+
let(:original) { Vector2d.new(-300, 300) }
|
|
412
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
413
|
+
|
|
414
|
+
its(:x) { is_expected.to eq(-200) }
|
|
415
|
+
its(:y) { is_expected.to eq(200) }
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
context "when a coordinate is negative and height is largest" do
|
|
419
|
+
let(:original) { Vector2d.new(300, -300) }
|
|
420
|
+
let(:comp) { Vector2d.new(150, 200) }
|
|
421
|
+
|
|
422
|
+
its(:x) { is_expected.to eq(200) }
|
|
423
|
+
its(:y) { is_expected.to eq(-200) }
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
context "when both coordinates are negative" do
|
|
427
|
+
let(:original) { Vector2d.new(-20, -10) }
|
|
428
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
429
|
+
|
|
430
|
+
its(:x) { is_expected.to eq(-10) }
|
|
431
|
+
its(:y) { is_expected.to eq(-5) }
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
context "when the constraint is negative" do
|
|
435
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
436
|
+
let(:comp) { Vector2d.new(-5, -5) }
|
|
437
|
+
|
|
438
|
+
its(:x) { is_expected.to eq(10) }
|
|
439
|
+
its(:y) { is_expected.to eq(5) }
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
context "with the zero vector" do
|
|
443
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
444
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
445
|
+
|
|
446
|
+
it "returns the zero vector" do
|
|
447
|
+
expect(vector).to eq(Vector2d.new(0, 0))
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
context "when the argument is invalid" do
|
|
452
|
+
let(:comp) { "garbage" }
|
|
453
|
+
|
|
454
|
+
it "raises an error" do
|
|
455
|
+
expect { vector }.to raise_error(ArgumentError)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
context "when the vector is zero and the argument is invalid" do
|
|
460
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
461
|
+
let(:comp) { "garbage" }
|
|
462
|
+
|
|
463
|
+
it "raises an error" do
|
|
464
|
+
expect { vector }.to raise_error(ArgumentError)
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
context "when the x axis is zero" do
|
|
469
|
+
let(:original) { Vector2d.new(0, 5) }
|
|
470
|
+
let(:comp) { Vector2d.new(10, 2) }
|
|
471
|
+
|
|
472
|
+
it "disregards the zero axis" do
|
|
473
|
+
expect(vector).to eq(Vector2d.new(0.0, 2.0))
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
it "matches #fit" do
|
|
477
|
+
expect(vector).to eq(original.fit(comp))
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
context "when the y axis is zero" do
|
|
482
|
+
let(:original) { Vector2d.new(5, 0) }
|
|
483
|
+
let(:comp) { Vector2d.new(2, 10) }
|
|
484
|
+
|
|
485
|
+
it "disregards the zero axis" do
|
|
486
|
+
expect(vector).to eq(Vector2d.new(2.0, 0.0))
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it "matches #fit" do
|
|
490
|
+
expect(vector).to eq(original.fit(comp))
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
context "when both constraint axes are zero" do
|
|
495
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
496
|
+
let(:comp) { Vector2d.new(0, 0) }
|
|
497
|
+
|
|
498
|
+
it "returns the vector unchanged" do
|
|
499
|
+
expect(vector).to eq(Vector2d.new(20, 10))
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "matches #fit" do
|
|
503
|
+
expect(vector).to eq(original.fit(comp))
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
context "when upscaling is enabled" do
|
|
508
|
+
subject(:vector) { original.cover(comp, upscale: true) }
|
|
509
|
+
|
|
510
|
+
let(:comp) { Vector2d.new(600, 450) }
|
|
511
|
+
|
|
512
|
+
its(:x) { is_expected.to eq(600) }
|
|
513
|
+
its(:y) { is_expected.to eq(600) }
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
context "when upscaling is disabled and the vector is larger" do
|
|
517
|
+
subject(:vector) { original.cover(comp, upscale: false) }
|
|
518
|
+
|
|
519
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
520
|
+
|
|
521
|
+
its(:x) { is_expected.to eq(200) }
|
|
522
|
+
its(:y) { is_expected.to eq(200) }
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
context "when upscaling is disabled and the vector already covers" do
|
|
526
|
+
subject(:vector) { original.cover(comp, upscale: false) }
|
|
527
|
+
|
|
528
|
+
let(:comp) { Vector2d.new(600, 450) }
|
|
529
|
+
|
|
530
|
+
it { is_expected.to equal(original) }
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
context "when upscaling is disabled and the vector covers exactly" do
|
|
534
|
+
subject(:vector) { original.cover(comp, upscale: false) }
|
|
535
|
+
|
|
536
|
+
let(:comp) { Vector2d.new(300, 200) }
|
|
537
|
+
|
|
538
|
+
it { is_expected.to equal(original) }
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
context "when upscaling is disabled and a single axis constrains" do
|
|
542
|
+
subject(:vector) { original.cover(comp, upscale: false) }
|
|
543
|
+
|
|
544
|
+
let(:original) { Vector2d.new(0, 300) }
|
|
545
|
+
let(:comp) { Vector2d.new(10, 600) }
|
|
546
|
+
|
|
547
|
+
it { is_expected.to equal(original) }
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
context "when upscaling is disabled and a single axis scales it down" do
|
|
551
|
+
subject(:vector) { original.cover(comp, upscale: false) }
|
|
552
|
+
|
|
553
|
+
let(:original) { Vector2d.new(0, 300) }
|
|
554
|
+
let(:comp) { Vector2d.new(10, 150) }
|
|
555
|
+
|
|
556
|
+
its(:x) { is_expected.to eq(0) }
|
|
557
|
+
its(:y) { is_expected.to eq(150) }
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
describe "#covers?" do
|
|
562
|
+
subject { original.covers?(comp) }
|
|
563
|
+
|
|
564
|
+
let(:original) { Vector2d.new(20, 10) }
|
|
565
|
+
|
|
566
|
+
context "when the vector is larger than the constraint" do
|
|
567
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
568
|
+
|
|
569
|
+
it { is_expected.to be(true) }
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
context "when the vector falls short on the y axis" do
|
|
573
|
+
let(:original) { Vector2d.new(20, 1) }
|
|
574
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
575
|
+
|
|
576
|
+
it { is_expected.to be(false) }
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
context "when the vector falls short on the x axis" do
|
|
580
|
+
let(:original) { Vector2d.new(1, 20) }
|
|
581
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
582
|
+
|
|
583
|
+
it { is_expected.to be(false) }
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
context "when the vector matches the constraint" do
|
|
587
|
+
let(:comp) { Vector2d.new(20, 10) }
|
|
588
|
+
|
|
589
|
+
it { is_expected.to be(true) }
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
context "when the coordinates are negative" do
|
|
593
|
+
let(:original) { Vector2d.new(-20, -10) }
|
|
594
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
595
|
+
|
|
596
|
+
it { is_expected.to be(true) }
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
context "when an axis of the vector is zero" do
|
|
600
|
+
let(:original) { Vector2d.new(0, 10) }
|
|
601
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
602
|
+
|
|
603
|
+
it { is_expected.to be(true) }
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
context "with the zero vector" do
|
|
607
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
608
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
609
|
+
|
|
610
|
+
it { is_expected.to be(true) }
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
it "coerces the constraint" do
|
|
614
|
+
expect(original.covers?("5x5")).to be(true)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it "is true exactly when #cover doesn't scale the vector up" do
|
|
618
|
+
[Vector2d.new(5, 5), Vector2d.new(40, 40),
|
|
619
|
+
Vector2d.new(20, 10)].each do |c|
|
|
620
|
+
expect(original.covers?(c))
|
|
621
|
+
.to be(original.cover(c, upscale: false) == original.cover(c))
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
describe "#fit_either" do
|
|
627
|
+
subject(:vector) { original.fit_either(comp) }
|
|
628
|
+
|
|
629
|
+
it_behaves_like "a deprecated method", "fit_either", "#cover" do
|
|
630
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
context "when width is largest" do
|
|
634
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
635
|
+
|
|
636
|
+
it "matches #cover" do
|
|
637
|
+
expect(vector).to eq(original.cover(comp))
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
describe "#contain" do
|
|
643
|
+
subject(:vector) { original.contain(comp) }
|
|
644
|
+
|
|
645
|
+
it_behaves_like "a deprecated method", "contain",
|
|
646
|
+
"other.fit(self, upscale: false)" do
|
|
647
|
+
let(:comp) { Vector2d.new(400, 300) }
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
context "when vector is smaller" do
|
|
651
|
+
let(:comp) { Vector2d.new(150, 100) }
|
|
652
|
+
|
|
653
|
+
its(:x) { is_expected.to eq(150) }
|
|
654
|
+
its(:y) { is_expected.to eq(100) }
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
context "when vector is wider" do
|
|
658
|
+
let(:comp) { Vector2d.new(400, 300) }
|
|
659
|
+
|
|
660
|
+
its(:x) { is_expected.to eq(300) }
|
|
661
|
+
its(:y) { is_expected.to eq(225) }
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
context "when vector is higher" do
|
|
665
|
+
let(:comp) { Vector2d.new(300, 400) }
|
|
666
|
+
|
|
667
|
+
its(:x) { is_expected.to eq(225) }
|
|
668
|
+
its(:y) { is_expected.to eq(300) }
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
context "when the argument is an array" do
|
|
672
|
+
let(:comp) { [400, 300] }
|
|
673
|
+
|
|
674
|
+
its(:x) { is_expected.to eq(300) }
|
|
675
|
+
its(:y) { is_expected.to eq(225) }
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
context "when the argument is a string" do
|
|
679
|
+
let(:comp) { "400x300" }
|
|
680
|
+
|
|
681
|
+
its(:x) { is_expected.to eq(300) }
|
|
682
|
+
its(:y) { is_expected.to eq(225) }
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
context "when the argument is a hash" do
|
|
686
|
+
let(:comp) { { x: 400, y: 300 } }
|
|
687
|
+
|
|
688
|
+
its(:x) { is_expected.to eq(300) }
|
|
689
|
+
its(:y) { is_expected.to eq(225) }
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
context "when the coerced argument already fits" do
|
|
693
|
+
let(:comp) { [150, 100] }
|
|
694
|
+
|
|
695
|
+
it { is_expected.to be_a(Vector2d) }
|
|
696
|
+
its(:x) { is_expected.to eq(150) }
|
|
697
|
+
its(:y) { is_expected.to eq(100) }
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
context "when the vector is negative and wider" do
|
|
701
|
+
let(:comp) { Vector2d.new(-400, 300) }
|
|
702
|
+
|
|
703
|
+
its(:x) { is_expected.to eq(-300) }
|
|
704
|
+
its(:y) { is_expected.to eq(225) }
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
context "when the vector is negative and higher" do
|
|
708
|
+
let(:comp) { Vector2d.new(300, -400) }
|
|
709
|
+
|
|
710
|
+
its(:x) { is_expected.to eq(225) }
|
|
711
|
+
its(:y) { is_expected.to eq(-300) }
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
context "when both coordinates are negative" do
|
|
715
|
+
let(:comp) { Vector2d.new(-400, -300) }
|
|
716
|
+
|
|
717
|
+
its(:x) { is_expected.to eq(-300) }
|
|
718
|
+
its(:y) { is_expected.to eq(-225) }
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
context "when the negative vector already fits" do
|
|
722
|
+
let(:comp) { Vector2d.new(-150, -100) }
|
|
723
|
+
|
|
724
|
+
its(:x) { is_expected.to eq(-150) }
|
|
725
|
+
its(:y) { is_expected.to eq(-100) }
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
context "when the vector is unconstrained" do
|
|
729
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
730
|
+
let(:comp) { Vector2d.new(40, 20) }
|
|
731
|
+
|
|
732
|
+
it "returns the argument unchanged" do
|
|
733
|
+
expect(vector).to eq(Vector2d.new(40, 20))
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
context "when one axis is unconstrained and the other fits" do
|
|
738
|
+
let(:original) { Vector2d.new(0, 10) }
|
|
739
|
+
let(:comp) { Vector2d.new(5, 5) }
|
|
740
|
+
|
|
741
|
+
it "does not scale the argument up" do
|
|
742
|
+
expect(vector).to eq(Vector2d.new(5, 5))
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
context "when one axis is unconstrained and the other does not fit" do
|
|
747
|
+
let(:original) { Vector2d.new(0, 10) }
|
|
748
|
+
let(:comp) { Vector2d.new(5, 20) }
|
|
749
|
+
|
|
750
|
+
it "scales the argument down" do
|
|
751
|
+
expect(vector).to eq(Vector2d.new(2.5, 10))
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
context "when both vectors are zero" do
|
|
756
|
+
let(:original) { Vector2d.new(0, 0) }
|
|
757
|
+
let(:comp) { Vector2d.new(0, 0) }
|
|
758
|
+
|
|
759
|
+
it "returns the argument unchanged" do
|
|
760
|
+
expect(vector).to eq(Vector2d.new(0, 0))
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
context "when the argument is invalid" do
|
|
765
|
+
let(:comp) { "garbage" }
|
|
766
|
+
|
|
767
|
+
it "raises an error" do
|
|
768
|
+
expect { vector }.to raise_error(ArgumentError)
|
|
769
|
+
end
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
describe "#constrain_both" do
|
|
774
|
+
subject(:vector) { original.constrain_both(comp) }
|
|
775
|
+
|
|
776
|
+
it_behaves_like "a deprecated method", "constrain_both", "#fit" do
|
|
777
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
context "when the vector is larger than the constraint" do
|
|
781
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
782
|
+
|
|
783
|
+
it "matches #fit" do
|
|
784
|
+
expect(vector).to eq(original.fit(comp))
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
context "when the vector is smaller than the constraint" do
|
|
789
|
+
let(:comp) { Vector2d.new(600, 600) }
|
|
790
|
+
|
|
791
|
+
it "scales up, as #fit does" do
|
|
792
|
+
expect(vector).to eq(Vector2d.new(600, 600))
|
|
793
|
+
end
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
describe "#constrain_one" do
|
|
798
|
+
subject(:vector) { original.constrain_one(comp) }
|
|
799
|
+
|
|
800
|
+
it_behaves_like "a deprecated method", "constrain_one", "#cover" do
|
|
801
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
context "when width is largest" do
|
|
805
|
+
let(:comp) { Vector2d.new(200, 150) }
|
|
806
|
+
|
|
807
|
+
it "matches #cover" do
|
|
808
|
+
expect(vector).to eq(original.cover(comp))
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
context "when height is largest" do
|
|
813
|
+
let(:comp) { Vector2d.new(150, 200) }
|
|
814
|
+
|
|
815
|
+
it "matches #cover" do
|
|
816
|
+
expect(vector).to eq(original.cover(comp))
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
end
|