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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +39 -0
  3. data/.github/workflows/build.yml +48 -10
  4. data/.gitignore +3 -1
  5. data/.release-please-manifest.json +3 -0
  6. data/.rubocop.yml +5 -2
  7. data/.tool-versions +1 -0
  8. data/.yardopts +11 -0
  9. data/CHANGELOG.md +224 -0
  10. data/CODE_OF_CONDUCT.md +92 -0
  11. data/CONTRIBUTING.md +30 -0
  12. data/Gemfile +6 -2
  13. data/Gemfile.lock +85 -51
  14. data/README.md +330 -13
  15. data/Rakefile +21 -0
  16. data/benchmark/vector_comparison.rb +129 -0
  17. data/lib/vector2d/angles.rb +315 -0
  18. data/lib/vector2d/arithmetic.rb +97 -0
  19. data/lib/vector2d/comparison.rb +188 -0
  20. data/lib/vector2d/componentwise.rb +239 -0
  21. data/lib/vector2d/constructors.rb +137 -0
  22. data/lib/vector2d/conversions.rb +139 -0
  23. data/lib/vector2d/coordinates.rb +22 -0
  24. data/lib/vector2d/deprecation.rb +16 -0
  25. data/lib/vector2d/dimensions.rb +297 -0
  26. data/lib/vector2d/interpolation.rb +191 -0
  27. data/lib/vector2d/lengths.rb +284 -0
  28. data/lib/vector2d/matrix_interop.rb +93 -0
  29. data/lib/vector2d/parsing.rb +142 -0
  30. data/lib/vector2d/projection.rb +208 -0
  31. data/lib/vector2d/version.rb +2 -1
  32. data/lib/vector2d.rb +174 -62
  33. data/release-please-config.json +10 -0
  34. data/spec/lib/vector2d/angles_spec.rb +441 -0
  35. data/spec/lib/vector2d/{calculations_spec.rb → arithmetic_spec.rb} +48 -58
  36. data/spec/lib/vector2d/comparison_spec.rb +358 -0
  37. data/spec/lib/vector2d/componentwise_spec.rb +300 -0
  38. data/spec/lib/vector2d/constructors_spec.rb +299 -0
  39. data/spec/lib/vector2d/conversions_spec.rb +234 -0
  40. data/spec/lib/vector2d/dimensions_spec.rb +820 -0
  41. data/spec/lib/vector2d/interpolation_spec.rb +322 -0
  42. data/spec/lib/vector2d/lengths_spec.rb +457 -0
  43. data/spec/lib/vector2d/matrix_interop_spec.rb +92 -0
  44. data/spec/lib/vector2d/parsing_spec.rb +329 -0
  45. data/spec/lib/vector2d/projection_spec.rb +306 -0
  46. data/spec/lib/vector2d_documentation_spec.rb +38 -0
  47. data/spec/lib/vector2d_immutability_spec.rb +263 -0
  48. data/spec/lib/vector2d_spec.rb +90 -50
  49. data/spec/lib/vector2d_subclassing_spec.rb +210 -0
  50. data/spec/lib/vector2d_tags_spec.rb +43 -0
  51. data/spec/spec_helper.rb +2 -0
  52. data/spec/support/doc_examples/comments.rb +65 -0
  53. data/spec/support/doc_examples/markdown.rb +50 -0
  54. data/spec/support/doc_examples.rb +94 -0
  55. data/spec/support/doc_tags.rb +151 -0
  56. data/spec/support/shared_examples/class_preserving_method.rb +10 -0
  57. data/spec/support/shared_examples/deprecated_method.rb +25 -0
  58. data/spec/support/shared_examples/parsed_vector.rb +11 -0
  59. data/vector2d.gemspec +9 -3
  60. metadata +55 -19
  61. data/.travis.yml +0 -10
  62. data/lib/vector2d/calculations.rb +0 -141
  63. data/lib/vector2d/coercions.rb +0 -64
  64. data/lib/vector2d/fitting.rb +0 -60
  65. data/lib/vector2d/properties.rb +0 -46
  66. data/lib/vector2d/transformations.rb +0 -85
  67. data/spec/lib/vector2d/coercions_spec.rb +0 -59
  68. data/spec/lib/vector2d/fitting_spec.rb +0 -70
  69. data/spec/lib/vector2d/properties_spec.rb +0 -47
  70. data/spec/lib/vector2d/transformations_spec.rb +0 -122
@@ -0,0 +1,441 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "bigdecimal"
5
+
6
+ describe Vector2d::Angles do
7
+ subject(:vector) { Vector2d.new(2, 3) }
8
+
9
+ describe ".radians" do
10
+ it "converts degrees to radians" do
11
+ expect(Vector2d.radians(90)).to be_within(0.0001).of(Math::PI / 2)
12
+ end
13
+
14
+ it "converts negative angles" do
15
+ expect(Vector2d.radians(-90)).to be_within(0.0001).of(-Math::PI / 2)
16
+ end
17
+
18
+ it "returns a float" do
19
+ expect(Vector2d.radians(0)).to be_an_instance_of(Float)
20
+ end
21
+
22
+ it "is the inverse of .degrees" do
23
+ expect(Vector2d.degrees(Vector2d.radians(37.5))).to be_within(0.0001).of(37.5)
24
+ end
25
+
26
+ it "raises an ArgumentError unless the angle is a real number" do
27
+ expect { Vector2d.radians(Complex(1, 2)) }
28
+ .to raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
29
+ end
30
+
31
+ [90, 90.0, Rational(90, 1), BigDecimal("90")].each do |angle|
32
+ context "with a #{angle.class} angle" do
33
+ subject(:radians) { Vector2d.radians(angle) }
34
+
35
+ it { is_expected.to be_an_instance_of(Float) }
36
+
37
+ it "converts the angle" do
38
+ expect(radians).to be_within(0.0001).of(Math::PI / 2)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".degrees" do
45
+ it "converts radians to degrees" do
46
+ expect(Vector2d.degrees(Math::PI / 2)).to be_within(0.0001).of(90.0)
47
+ end
48
+
49
+ it "converts negative angles" do
50
+ expect(Vector2d.degrees(-Math::PI / 2)).to be_within(0.0001).of(-90.0)
51
+ end
52
+
53
+ it "returns a float" do
54
+ expect(Vector2d.degrees(0)).to be_an_instance_of(Float)
55
+ end
56
+
57
+ it "raises an ArgumentError unless the angle is a real number" do
58
+ expect { Vector2d.degrees(Complex(1, 2)) }
59
+ .to raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
60
+ end
61
+
62
+ [1, 1.0, Rational(1, 1), BigDecimal("1")].each do |angle|
63
+ context "with a #{angle.class} angle" do
64
+ subject(:degrees) { Vector2d.degrees(angle) }
65
+
66
+ it { is_expected.to be_an_instance_of(Float) }
67
+
68
+ it "converts the angle" do
69
+ expect(degrees).to be_within(0.0001).of(57.2957)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ describe ".from_degrees" do
76
+ it "creates a vector from an angle" do
77
+ expect(Vector2d.from_degrees(90).y).to be_within(0.0001).of(1.0)
78
+ end
79
+
80
+ it "defaults to unit length" do
81
+ expect(Vector2d.from_degrees(30).length).to be_within(0.0001).of(1.0)
82
+ end
83
+
84
+ it "takes a length" do
85
+ expect(Vector2d.from_degrees(30, 2.0).length).to be_within(0.0001).of(2.0)
86
+ end
87
+
88
+ it "matches .from_angle with the angle converted" do
89
+ expect(Vector2d.from_degrees(30, 2.0))
90
+ .to eq(Vector2d.from_angle(Vector2d.radians(30), 2.0))
91
+ end
92
+
93
+ it "raises an ArgumentError unless the angle is a real number" do
94
+ expect { Vector2d.from_degrees(Complex(1, 2)) }
95
+ .to raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
96
+ end
97
+
98
+ it "raises an ArgumentError unless the length is a real number" do
99
+ expect { Vector2d.from_degrees(30, Complex(1, 2)) }
100
+ .to raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
101
+ end
102
+
103
+ [45, 45.0, Rational(45, 1), BigDecimal("45")].each do |angle|
104
+ context "with a #{angle.class} angle" do
105
+ subject(:vector) { Vector2d.from_degrees(angle) }
106
+
107
+ it "returns float coordinates" do
108
+ expect(vector.to_a).to all(be_an_instance_of(Float))
109
+ end
110
+
111
+ it "converts the angle" do
112
+ expect(vector.angle_in_degrees).to be_within(0.0001).of(45.0)
113
+ end
114
+ end
115
+ end
116
+
117
+ [2, 2.0, Rational(2, 1), BigDecimal("2")].each do |length|
118
+ context "with a #{length.class} length" do
119
+ subject(:vector) { Vector2d.from_degrees(45, length) }
120
+
121
+ it "returns float coordinates" do
122
+ expect(vector.to_a).to all(be_an_instance_of(Float))
123
+ end
124
+
125
+ it "applies the length" do
126
+ expect(vector.length).to be_within(0.0001).of(2.0)
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe ".angle_to" do
133
+ let(:v1) { Vector2d.new(2, 3) }
134
+ let(:v2) { Vector2d.new(4, 5) }
135
+
136
+ it "calculates the angle from one vector to another" do
137
+ expect(Vector2d.angle_to(v1, v2)).to be_within(0.0001).of(-0.0867)
138
+ end
139
+
140
+ it "signs the angle by direction of rotation" do
141
+ expect(Vector2d.angle_to(v2, v1)).to be_within(0.0001).of(0.0867)
142
+ end
143
+
144
+ it "ignores the magnitude of the vectors" do
145
+ expect(
146
+ Vector2d.angle_to(v1 * 1000, v2 * 0.001)
147
+ ).to be_within(0.0001).of(-0.0867)
148
+ end
149
+
150
+ it "returns zero for identical vectors" do
151
+ expect(Vector2d.angle_to(v1, v1)).to eq(0.0)
152
+ end
153
+
154
+ it "returns zero for parallel vectors" do
155
+ expect(Vector2d.angle_to(v1, v1 * 2.3)).to eq(0.0)
156
+ end
157
+
158
+ it "returns PI for antiparallel vectors" do
159
+ expect(Vector2d.angle_to(v1, v1 * -2.3)).to eq(Math::PI)
160
+ end
161
+
162
+ it "returns zero for a zero length vector" do
163
+ expect(Vector2d.angle_to(v1, Vector2d.new(0, 0))).to eq(0.0)
164
+ end
165
+
166
+ it "stays within -PI..PI" do
167
+ angles = 360.times.map do |i|
168
+ Vector2d.angle_to(v1, v1.rotate(i * Math::PI / 180))
169
+ end
170
+ expect(angles).to all(be_between(-Math::PI, Math::PI))
171
+ end
172
+
173
+ it "handles parallel vectors of any magnitude" do
174
+ angles = 1000.times.map do |i|
175
+ v = Vector2d.new((i + 1) * 0.37, (i + 1) * -1.13)
176
+ Vector2d.angle_to(v, v * ((i % 7) + 1))
177
+ end
178
+ expect(angles).to all(be_within(1e-12).of(0.0))
179
+ end
180
+ end
181
+
182
+ describe ".angle_between" do
183
+ let(:v1) { Vector2d.new(2, 3) }
184
+ let(:v2) { Vector2d.new(4, 5) }
185
+
186
+ it "calculates the unsigned angle between two vectors" do
187
+ expect(Vector2d.angle_between(v1, v2)).to be_within(0.0001).of(0.0867)
188
+ end
189
+
190
+ it "ignores the order of the arguments" do
191
+ expect(Vector2d.angle_between(v2, v1))
192
+ .to eq(Vector2d.angle_between(v1, v2))
193
+ end
194
+
195
+ it "ignores the magnitude of the vectors" do
196
+ expect(
197
+ Vector2d.angle_between(v1 * 1000, v2 * 0.001)
198
+ ).to be_within(0.0001).of(0.0867)
199
+ end
200
+
201
+ it "returns zero for parallel vectors" do
202
+ expect(Vector2d.angle_between(v1, v1 * 2.3)).to eq(0.0)
203
+ end
204
+
205
+ it "returns PI for antiparallel vectors" do
206
+ expect(Vector2d.angle_between(v1, v1 * -2.3)).to eq(Math::PI)
207
+ end
208
+
209
+ it "returns zero for a zero length vector" do
210
+ expect(Vector2d.angle_between(v1, Vector2d.new(0, 0))).to eq(0.0)
211
+ end
212
+
213
+ it "stays within 0..PI" do
214
+ angles = 360.times.map do |i|
215
+ Vector2d.angle_between(v1, v1.rotate(i * Math::PI / 180))
216
+ end
217
+ expect(angles).to all(be_between(0, Math::PI))
218
+ end
219
+ end
220
+
221
+ describe "#angle" do
222
+ it "returns the angle" do
223
+ expect(vector.angle).to be_within(0.0001).of(0.9827)
224
+ end
225
+ end
226
+
227
+ describe "#angle_in_degrees" do
228
+ it "returns the angle in degrees" do
229
+ expect(vector.angle_in_degrees).to be_within(0.0001).of(56.3099)
230
+ end
231
+
232
+ it "is signed" do
233
+ expect(Vector2d.new(0, -1).angle_in_degrees).to be_within(0.0001).of(-90.0)
234
+ end
235
+
236
+ it "matches #angle converted" do
237
+ expect(vector.angle_in_degrees).to eq(Vector2d.degrees(vector.angle))
238
+ end
239
+
240
+ it "is zero for the zero vector" do
241
+ expect(Vector2d.new(0, 0).angle_in_degrees).to eq(0.0)
242
+ end
243
+
244
+ [[0, 1], [0.0, 1.0], [Rational(0, 1), Rational(1, 1)],
245
+ [BigDecimal("0"), BigDecimal("1")]].each do |(x, y)|
246
+ context "with #{y.class} coordinates" do
247
+ subject(:degrees) { Vector2d.new(x, y).angle_in_degrees }
248
+
249
+ it { is_expected.to be_an_instance_of(Float) }
250
+
251
+ it "returns the angle in degrees" do
252
+ expect(degrees).to be_within(0.0001).of(90.0)
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ describe "#angle_to" do
259
+ let(:comp) { Vector2d.new(3, 4) }
260
+
261
+ it "calculates the angle to the other vector" do
262
+ expect(
263
+ vector.angle_to(comp)
264
+ ).to eq(Vector2d.angle_to(vector, comp))
265
+ end
266
+
267
+ it "coerces the argument" do
268
+ expect(vector.angle_to([3, 4])).to eq(vector.angle_to(comp))
269
+ end
270
+ end
271
+
272
+ describe "#angle_between" do
273
+ let(:comp) { Vector2d.new(3, 4) }
274
+
275
+ it "calculates the angle between vectors" do
276
+ expect(
277
+ vector.angle_between(comp)
278
+ ).to eq(Vector2d.angle_between(vector, comp))
279
+ end
280
+
281
+ it "coerces the argument" do
282
+ expect(vector.angle_between([3, 4])).to eq(vector.angle_between(comp))
283
+ end
284
+ end
285
+
286
+ describe "#angle_with" do
287
+ let(:comp) { Vector2d.new(3, 4) }
288
+
289
+ it "is an alias of #angle_between" do
290
+ expect(vector.angle_with(comp)).to eq(vector.angle_between(comp))
291
+ end
292
+ end
293
+
294
+ describe "#to_polar" do
295
+ it "returns the length first" do
296
+ expect(vector.to_polar.first).to be_within(0.0001).of(3.6055)
297
+ end
298
+
299
+ it "returns the angle second" do
300
+ expect(vector.to_polar.last).to be_within(0.0001).of(0.9827)
301
+ end
302
+
303
+ it "is the inverse of Vector2d.from_angle" do
304
+ length, angle = vector.to_polar
305
+
306
+ expect(Vector2d.from_angle(angle, length))
307
+ .to eq(Vector2d.new(2.0, 3.0))
308
+ end
309
+
310
+ context "with the zero vector" do
311
+ let(:vector) { Vector2d.new(0, 0) }
312
+
313
+ it "returns zero length and angle" do
314
+ expect(vector.to_polar).to eq([0.0, 0.0])
315
+ end
316
+ end
317
+ end
318
+
319
+ describe "#rotate" do
320
+ subject { vector.rotate(rotation).round(3) }
321
+
322
+ let(:vector) { Vector2d.new(1, 0) }
323
+
324
+ context "when roating by PI" do
325
+ let(:rotation) { Math::PI }
326
+
327
+ it { is_expected.to eq(Vector2d.new(-1, 0)) }
328
+ end
329
+
330
+ context "when roating by PI/2" do
331
+ let(:rotation) { Math::PI / 2 }
332
+
333
+ it { is_expected.to eq(Vector2d.new(0, 1)) }
334
+ end
335
+
336
+ context "when roating by -PI/2" do
337
+ let(:rotation) { -Math::PI / 2 }
338
+
339
+ it { is_expected.to eq(Vector2d.new(0, -1)) }
340
+ end
341
+
342
+ context "when roating by PI/4" do
343
+ let(:rotation) { Math::PI / 4 }
344
+
345
+ it { is_expected.to eq(Vector2d.new(0.707, 0.707)) }
346
+ end
347
+
348
+ context "with a complex angle" do
349
+ it "raises an error" do
350
+ expect { vector.rotate(Complex(1, 2)) }.to(
351
+ raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
352
+ )
353
+ end
354
+ end
355
+ end
356
+
357
+ describe "#rotate_degrees" do
358
+ it "rotates the vector counterclockwise" do
359
+ expect(vector.rotate_degrees(90)).to eq(Vector2d.new(-3.0, 2.0))
360
+ end
361
+
362
+ it "rotates clockwise for a negative angle" do
363
+ expect(vector.rotate_degrees(-90).x).to be_within(0.0001).of(3.0)
364
+ end
365
+
366
+ it "matches #rotate with the angle converted" do
367
+ expect(vector.rotate_degrees(30)).to eq(vector.rotate(Vector2d.radians(30)))
368
+ end
369
+
370
+ it "leaves the length alone" do
371
+ expect(vector.rotate_degrees(30).length).to be_within(0.0001).of(vector.length)
372
+ end
373
+
374
+ it "raises an ArgumentError unless the angle is a real number" do
375
+ expect { vector.rotate_degrees(Complex(1, 2)) }
376
+ .to raise_error(ArgumentError, "not a valid coordinate: (1+2i)")
377
+ end
378
+
379
+ [90, 90.0, Rational(90, 1), BigDecimal("90")].each do |angle|
380
+ context "with a #{angle.class} angle" do
381
+ subject(:rotated) { vector.rotate_degrees(angle) }
382
+
383
+ it "returns float coordinates" do
384
+ expect(rotated.to_a).to all(be_an_instance_of(Float))
385
+ end
386
+
387
+ it "rotates the vector" do
388
+ expect(rotated).to eq(Vector2d.new(-3.0, 2.0))
389
+ end
390
+ end
391
+ end
392
+ end
393
+
394
+ describe "#rotate_around" do
395
+ subject { vector.rotate_around(center, Math::PI / 2).round(3) }
396
+
397
+ let(:vector) { Vector2d.new(2, 1) }
398
+ let(:center) { Vector2d.new(1, 1) }
399
+
400
+ it { is_expected.to eq(Vector2d.new(1, 2)) }
401
+
402
+ context "when the center is the origin" do
403
+ let(:center) { Vector2d.new(0, 0) }
404
+
405
+ it { is_expected.to eq(vector.rotate(Math::PI / 2).round(3)) }
406
+ end
407
+
408
+ context "when the center is the vector itself" do
409
+ let(:center) { vector }
410
+
411
+ it { is_expected.to eq(vector) }
412
+ end
413
+
414
+ context "with a coercible center" do
415
+ let(:center) { [1, 1] }
416
+
417
+ it { is_expected.to eq(Vector2d.new(1, 2)) }
418
+ end
419
+ end
420
+
421
+ describe "#perpendicular" do
422
+ it "returns a perpendicular vector" do
423
+ expect(vector.perpendicular).to eq(Vector2d.new(-3, 2))
424
+ end
425
+
426
+ it "turns the same way as #rotate" do
427
+ expect(vector.perpendicular.round(3))
428
+ .to eq(vector.rotate(Math::PI / 2).round(3))
429
+ end
430
+ end
431
+
432
+ describe "#perpendicular_cw" do
433
+ it "returns a perpendicular vector" do
434
+ expect(vector.perpendicular_cw).to eq(Vector2d.new(3, -2))
435
+ end
436
+
437
+ it "turns the opposite way from #perpendicular" do
438
+ expect(vector.perpendicular_cw).to eq(vector.perpendicular.reverse)
439
+ end
440
+ end
441
+ end
@@ -2,36 +2,9 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- describe Vector2d::Calculations do
5
+ describe Vector2d::Arithmetic do
6
6
  subject(:vector) { Vector2d.new(2, 3) }
7
7
 
8
- describe ".cross_product" do
9
- let(:v1) { Vector2d.new(2, 3) }
10
- let(:v2) { Vector2d.new(4, 5) }
11
-
12
- it "calculates the cross product between two vectors" do
13
- expect(Vector2d.cross_product(v1, v2)).to eq(-2.0)
14
- end
15
- end
16
-
17
- describe ".dot_product" do
18
- let(:v1) { Vector2d.new(2, 3) }
19
- let(:v2) { Vector2d.new(4, 5) }
20
-
21
- it "calculates the dot product between two vectors" do
22
- expect(Vector2d.dot_product(v1, v2)).to eq(23)
23
- end
24
- end
25
-
26
- describe ".angle_between" do
27
- let(:v1) { Vector2d.new(2, 3) }
28
- let(:v2) { Vector2d.new(4, 5) }
29
-
30
- it "calculates the angle between two vectors" do
31
- expect(Vector2d.angle_between(v1, v2)).to be_within(0.0001).of(0.0867)
32
- end
33
- end
34
-
35
8
  describe "*" do
36
9
  context "with vector" do
37
10
  subject(:vector) { Vector2d.new(2, 3) * Vector2d.new(3, 4) }
@@ -47,6 +20,28 @@ describe Vector2d::Calculations do
47
20
  it "multiplies both members" do
48
21
  expect(vector).to eq(Vector2d.new(6, 9))
49
22
  end
23
+
24
+ its(:x) { is_expected.to be_a(Integer) }
25
+ its(:y) { is_expected.to be_a(Integer) }
26
+ end
27
+
28
+ context "with a float" do
29
+ subject(:vector) { Vector2d.new(2, 3) * 3.0 }
30
+
31
+ it "multiplies both members" do
32
+ expect(vector).to eq(Vector2d.new(6.0, 9.0))
33
+ end
34
+
35
+ its(:x) { is_expected.to be_a(Float) }
36
+ its(:y) { is_expected.to be_a(Float) }
37
+ end
38
+
39
+ context "with a complex number" do
40
+ it "raises a TypeError" do
41
+ expect { Vector2d.new(2, 3) * Complex(1, 2) }.to(
42
+ raise_error(TypeError, "Complex can't be coerced into Vector2d")
43
+ )
44
+ end
50
45
  end
51
46
  end
52
47
 
@@ -65,6 +60,9 @@ describe Vector2d::Calculations do
65
60
  it "divides both members" do
66
61
  expect(vector).to eq(Vector2d.new(2, 4))
67
62
  end
63
+
64
+ its(:x) { is_expected.to be_a(Integer) }
65
+ its(:y) { is_expected.to be_a(Integer) }
68
66
  end
69
67
  end
70
68
 
@@ -83,6 +81,9 @@ describe Vector2d::Calculations do
83
81
  it "adds to both members" do
84
82
  expect(vector).to eq(Vector2d.new(4, 5))
85
83
  end
84
+
85
+ its(:x) { is_expected.to be_a(Integer) }
86
+ its(:y) { is_expected.to be_a(Integer) }
86
87
  end
87
88
  end
88
89
 
@@ -101,50 +102,39 @@ describe Vector2d::Calculations do
101
102
  it "subtracts from both members" do
102
103
  expect(vector).to eq(Vector2d.new(0, 1))
103
104
  end
104
- end
105
- end
106
105
 
107
- describe "#cross_product" do
108
- let(:comp) { Vector2d.new(3, 4) }
109
-
110
- it "calulates the cross product" do
111
- expect(
112
- vector.cross_product(comp)
113
- ).to eq(Vector2d.cross_product(vector, comp))
106
+ its(:x) { is_expected.to be_a(Integer) }
107
+ its(:y) { is_expected.to be_a(Integer) }
114
108
  end
115
109
  end
116
110
 
117
- describe "#distance" do
118
- let(:comp) { Vector2d.new(3, 4) }
119
-
120
- it "returns the distance between two vectors" do
121
- expect(vector.distance(comp)).to be_within(0.0001).of(1.4142)
111
+ describe "-@" do
112
+ it "reverses the vector" do
113
+ expect(-Vector2d.new(2, 3)).to eq(Vector2d.new(-2, -3))
122
114
  end
123
- end
124
115
 
125
- describe "#squared_distance" do
126
- let(:comp) { Vector2d.new(5, 6) }
116
+ it "is the same as #reverse" do
117
+ expect(-vector).to eq(vector.reverse)
118
+ end
127
119
 
128
- it "returns the squared distance between two vectors" do
129
- expect(vector.squared_distance(comp)).to eq(18)
120
+ it "returns the original vector when applied twice" do
121
+ expect(-(-vector)).to eq(vector)
130
122
  end
131
123
  end
132
124
 
133
- describe "#dot_product" do
134
- let(:comp) { Vector2d.new(3, 4) }
125
+ describe "+@" do
126
+ it "returns the vector unchanged" do
127
+ expect(+Vector2d.new(2, 3)).to eq(Vector2d.new(2, 3))
128
+ end
135
129
 
136
- it "calulates the dot product" do
137
- expect(vector.dot_product(comp)).to eq(Vector2d.dot_product(vector, comp))
130
+ it "returns the same object" do
131
+ expect(+vector).to be(vector)
138
132
  end
139
133
  end
140
134
 
141
- describe "#angle_between" do
142
- let(:comp) { Vector2d.new(3, 4) }
143
-
144
- it "calculates the angle between vectors" do
145
- expect(
146
- vector.angle_between(comp)
147
- ).to eq(Vector2d.angle_between(vector, comp))
135
+ describe "#reverse" do
136
+ it "reverses the vector" do
137
+ expect(vector.reverse).to eq(Vector2d.new(-2, -3))
148
138
  end
149
139
  end
150
140
  end