vector2d 2.3.0 → 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/workflows/build.yml +1 -1
- data/.gitignore +3 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -1
- data/.yardopts +11 -0
- data/CHANGELOG.md +216 -0
- data/Gemfile +5 -1
- data/Gemfile.lock +35 -5
- data/README.md +325 -12
- data/Rakefile +18 -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 +172 -60
- 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 +2 -1
- metadata +42 -13
- 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 -98
- 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 -139
data/lib/vector2d/coercions.rb
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Vector2d
|
|
4
|
-
module Coercions
|
|
5
|
-
def coerce(other)
|
|
6
|
-
case other
|
|
7
|
-
when Vector2d
|
|
8
|
-
[other, self]
|
|
9
|
-
when Array, Numeric, String, Hash
|
|
10
|
-
[Vector2d.parse(other), self]
|
|
11
|
-
else
|
|
12
|
-
raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Renders vector as a pretty string.
|
|
17
|
-
#
|
|
18
|
-
# Vector2d(2, 3).inspect # => "Vector2d(2,3)"
|
|
19
|
-
#
|
|
20
|
-
def inspect
|
|
21
|
-
"Vector2d(#{x},#{y})"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Converts vector to array.
|
|
25
|
-
#
|
|
26
|
-
# Vector2d(2, 3).to_a # => [2,3]
|
|
27
|
-
#
|
|
28
|
-
def to_a
|
|
29
|
-
[x, y]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Converts vector to hash.
|
|
33
|
-
#
|
|
34
|
-
# Vector2d(2, 3).to_hash # => {x: 2, y: 3}
|
|
35
|
-
#
|
|
36
|
-
def to_hash
|
|
37
|
-
{ x: x, y: y }
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Converts vector to fixnums.
|
|
41
|
-
#
|
|
42
|
-
# Vector2d(2.0, 3.0).to_i_vector # => Vector2d(2,3)
|
|
43
|
-
#
|
|
44
|
-
def to_i_vector
|
|
45
|
-
self.class.new(x.to_i, y.to_i)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Converts vector to floats.
|
|
49
|
-
#
|
|
50
|
-
# Vector2d(2, 3).to_f_vector # => Vector2d(2.0,3.0)
|
|
51
|
-
#
|
|
52
|
-
def to_f_vector
|
|
53
|
-
self.class.new(x.to_f, y.to_f)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Converts vector to string.
|
|
57
|
-
#
|
|
58
|
-
# Vector2d.new(150, 100).to_s # => "150x100"
|
|
59
|
-
#
|
|
60
|
-
def to_s
|
|
61
|
-
"#{x}x#{y}"
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
data/lib/vector2d/fitting.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Vector2d
|
|
4
|
-
module Fitting
|
|
5
|
-
# Scales down the given vector unless it fits inside.
|
|
6
|
-
#
|
|
7
|
-
# vector = Vector2d(20, 20)
|
|
8
|
-
# vector.contain(Vector2d(10, 10)) # => Vector2d(10,10)
|
|
9
|
-
# vector.contain(Vector2d(40, 20)) # => Vector2d(20,10)
|
|
10
|
-
# vector.contain(Vector2d(20, 40)) # => Vector2d(10,20)
|
|
11
|
-
#
|
|
12
|
-
def contain(other)
|
|
13
|
-
v, = coerce(other)
|
|
14
|
-
v.x > x || v.y > y ? other.fit(self) : other
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Scales the vector to fit inside another vector, retaining the
|
|
18
|
-
# aspect ratio.
|
|
19
|
-
#
|
|
20
|
-
# vector = Vector2d(20, 10)
|
|
21
|
-
# vector.fit(Vector2d(10, 10)) # => Vector2d(10,5)
|
|
22
|
-
# vector.fit(Vector2d(20, 20)) # => Vector2d(20,10)
|
|
23
|
-
# vector.fit(Vector2d(40, 40)) # => Vector2d(40,20)
|
|
24
|
-
#
|
|
25
|
-
# Note: Either axis will be disregarded if zero or nil. This is a
|
|
26
|
-
# feature, not a bug.
|
|
27
|
-
#
|
|
28
|
-
def fit(other)
|
|
29
|
-
v, = coerce(other)
|
|
30
|
-
scale = v.to_f_vector / self
|
|
31
|
-
self * (
|
|
32
|
-
if scale.y.zero? || (scale.x.positive? && scale.x < scale.y)
|
|
33
|
-
scale.x
|
|
34
|
-
else
|
|
35
|
-
scale.y
|
|
36
|
-
end
|
|
37
|
-
)
|
|
38
|
-
end
|
|
39
|
-
alias constrain_both fit
|
|
40
|
-
|
|
41
|
-
# Constrain/expand so that one of the coordinates fit within (the
|
|
42
|
-
# square implied by) another vector.
|
|
43
|
-
#
|
|
44
|
-
# constraint = Vector2d(5, 5)
|
|
45
|
-
# Vector2d(20, 10).fit_either(constraint) # => Vector2d(10,5)
|
|
46
|
-
# Vector2d(10, 20).fit_either(constraint) # => Vector2d(5,10)
|
|
47
|
-
#
|
|
48
|
-
def fit_either(other)
|
|
49
|
-
v, = coerce(other)
|
|
50
|
-
scale = v.to_f_vector / self
|
|
51
|
-
if scale.x.positive? && scale.y.positive?
|
|
52
|
-
scale = [scale.x, scale.y].max
|
|
53
|
-
self * scale
|
|
54
|
-
else
|
|
55
|
-
fit(v)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
alias constrain_one fit_either
|
|
59
|
-
end
|
|
60
|
-
end
|
data/lib/vector2d/properties.rb
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Vector2d
|
|
4
|
-
module Properties
|
|
5
|
-
# Angle of vector.
|
|
6
|
-
#
|
|
7
|
-
# Vector2d(2, 3).angle # => 0.9827..
|
|
8
|
-
#
|
|
9
|
-
def angle
|
|
10
|
-
Math.atan2(y, x)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Aspect ratio of vector.
|
|
14
|
-
#
|
|
15
|
-
# Vector2d(2, 3).aspect_ratio # => 0.6667..
|
|
16
|
-
#
|
|
17
|
-
def aspect_ratio
|
|
18
|
-
(x.to_f / y).abs
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Length of vector.
|
|
22
|
-
#
|
|
23
|
-
# Vector2d(2, 3).length # => 3.6055..
|
|
24
|
-
#
|
|
25
|
-
def length
|
|
26
|
-
Math.sqrt(squared_length)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Squared length of vector.
|
|
30
|
-
#
|
|
31
|
-
# Vector2d(2, 3).squared_length # => 13
|
|
32
|
-
#
|
|
33
|
-
def squared_length
|
|
34
|
-
(x * x) + (y * y)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Is this a normalized vector?
|
|
38
|
-
#
|
|
39
|
-
# Vector2d(0, 1).normalized? # => true
|
|
40
|
-
# Vector2d(2, 3).normalized? # => false
|
|
41
|
-
#
|
|
42
|
-
def normalized?
|
|
43
|
-
(length.to_f - 1.0).abs < Float::EPSILON
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Vector2d
|
|
4
|
-
module Transformations
|
|
5
|
-
# Rounds vector to up nearest integer.
|
|
6
|
-
#
|
|
7
|
-
# Vector2d(2.4, 3.6).ceil # => Vector2d(3,4)
|
|
8
|
-
#
|
|
9
|
-
def ceil
|
|
10
|
-
self.class.new(x.ceil, y.ceil)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Clamps the vector between two others, one axis at a time.
|
|
14
|
-
# The bounds are coerced, so scalars work too.
|
|
15
|
-
#
|
|
16
|
-
# vector = Vector2d(2, 8)
|
|
17
|
-
# vector.clamp(Vector2d(3, 3), Vector2d(6, 6)) # => Vector2d(3,6)
|
|
18
|
-
# vector.clamp(3, 6) # => Vector2d(3,6)
|
|
19
|
-
#
|
|
20
|
-
def clamp(min, max)
|
|
21
|
-
min_v, = coerce(min)
|
|
22
|
-
max_v, = coerce(max)
|
|
23
|
-
self.class.new(x.clamp(min_v.x, max_v.x), y.clamp(min_v.y, max_v.y))
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Rounds vector to up nearest integer.
|
|
27
|
-
#
|
|
28
|
-
# Vector2d(2.4, 3.6).floor # => Vector2d(2,3)
|
|
29
|
-
#
|
|
30
|
-
def floor
|
|
31
|
-
self.class.new(x.floor, y.floor)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Normalizes the vector.
|
|
35
|
-
#
|
|
36
|
-
# vector = Vector2d(2, 3)
|
|
37
|
-
# vector.normalize # => Vector2d(0.5547.., 0.8320..)
|
|
38
|
-
# vector.normalize.length # => 1.0
|
|
39
|
-
#
|
|
40
|
-
def normalize
|
|
41
|
-
resize(1.0)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Returns a perpendicular vector.
|
|
45
|
-
#
|
|
46
|
-
# Vector2d(2, 3).perpendicular # => Vector2d(-3,2)
|
|
47
|
-
#
|
|
48
|
-
def perpendicular
|
|
49
|
-
Vector2d.new(-y, x)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Changes magnitude of vector.
|
|
53
|
-
#
|
|
54
|
-
# Vector2d(2, 3).resize(1.0) # => Vector2d(0.5547.., 0.8320..)
|
|
55
|
-
#
|
|
56
|
-
def resize(new_length)
|
|
57
|
-
self * (new_length / length)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Reverses the vector.
|
|
61
|
-
#
|
|
62
|
-
# Vector2d(2, 3).reverse # => Vector2d(-2,-3)
|
|
63
|
-
#
|
|
64
|
-
def reverse
|
|
65
|
-
self.class.new(-x, -y)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Rotates the vector
|
|
69
|
-
#
|
|
70
|
-
# Vector2d(1, 0).rotate(Math:PI/2) => Vector2d(1,0)
|
|
71
|
-
#
|
|
72
|
-
def rotate(angle)
|
|
73
|
-
Vector2d.new(
|
|
74
|
-
(x * Math.cos(angle)) - (y * Math.sin(angle)),
|
|
75
|
-
(x * Math.sin(angle)) + (y * Math.cos(angle))
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Rounds vector to nearest integer.
|
|
80
|
-
#
|
|
81
|
-
# Vector2d(2.4, 3.6).round # => Vector2d(2,4)
|
|
82
|
-
# Vector2d(2.4444, 3.666).round(2) # => Vector2d(2.44, 3.67)
|
|
83
|
-
#
|
|
84
|
-
def round(digits = 0)
|
|
85
|
-
self.class.new(x.round(digits), y.round(digits))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Truncates to max length if vector is longer than max.
|
|
89
|
-
#
|
|
90
|
-
# vector = Vector2d(2.0, 3.0)
|
|
91
|
-
# vector.truncate(5.0) # => Vector2d(2.0, 3.0)
|
|
92
|
-
# vector.truncate(1.0) # => Vector2d(0.5547.., 0.8320..)
|
|
93
|
-
#
|
|
94
|
-
def truncate(max)
|
|
95
|
-
resize([max, length].min)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Vector2d::Coercions do
|
|
6
|
-
subject(:vector) { Vector2d.new(2, 3) }
|
|
7
|
-
|
|
8
|
-
describe "#inspect" do
|
|
9
|
-
it "renders a string representation" do
|
|
10
|
-
expect(vector.inspect).to eq("Vector2d(2,3)")
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
describe "#to_a" do
|
|
15
|
-
it "returns an array" do
|
|
16
|
-
expect(vector.to_a).to eq([2, 3])
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe "#to_f_vector" do
|
|
21
|
-
subject { vector.to_f_vector }
|
|
22
|
-
|
|
23
|
-
its(:x) { is_expected.to be_a(Float) }
|
|
24
|
-
its(:y) { is_expected.to be_a(Float) }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
describe "#to_hash" do
|
|
28
|
-
it "returns a hash" do
|
|
29
|
-
expect(vector.to_hash).to eq(x: 2, y: 3)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe "#to_i_vector" do
|
|
34
|
-
subject { vector.to_i_vector }
|
|
35
|
-
|
|
36
|
-
let(:vector) { Vector2d.new(2.0, 3.0) }
|
|
37
|
-
|
|
38
|
-
its(:x) { is_expected.to be_a(Integer) }
|
|
39
|
-
its(:y) { is_expected.to be_a(Integer) }
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
describe "#to_s" do
|
|
43
|
-
context "when fixnum" do
|
|
44
|
-
subject(:vector) { Vector2d.new(2, 3) }
|
|
45
|
-
|
|
46
|
-
it "renders a string" do
|
|
47
|
-
expect(vector.to_s).to eq("2x3")
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context "when float" do
|
|
52
|
-
subject(:vector) { Vector2d.new(2.0, 3.0) }
|
|
53
|
-
|
|
54
|
-
it "renders a string" do
|
|
55
|
-
expect(vector.to_s).to eq("2.0x3.0")
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Vector2d::Fitting do
|
|
6
|
-
let(:original) { Vector2d.new(300, 300) }
|
|
7
|
-
|
|
8
|
-
describe "#contain" do
|
|
9
|
-
subject(:vector) { original.contain(comp) }
|
|
10
|
-
|
|
11
|
-
context "when vector is smaller" do
|
|
12
|
-
let(:comp) { Vector2d.new(150, 100) }
|
|
13
|
-
|
|
14
|
-
its(:x) { is_expected.to eq(150) }
|
|
15
|
-
its(:y) { is_expected.to eq(100) }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
context "when vector is wider" do
|
|
19
|
-
let(:comp) { Vector2d.new(400, 300) }
|
|
20
|
-
|
|
21
|
-
its(:x) { is_expected.to eq(300) }
|
|
22
|
-
its(:y) { is_expected.to eq(225) }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
context "when vector is higher" do
|
|
26
|
-
let(:comp) { Vector2d.new(300, 400) }
|
|
27
|
-
|
|
28
|
-
its(:x) { is_expected.to eq(225) }
|
|
29
|
-
its(:y) { is_expected.to eq(300) }
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe "#fit" do
|
|
34
|
-
subject(:vector) { original.fit(comp) }
|
|
35
|
-
|
|
36
|
-
context "when scaling by height" do
|
|
37
|
-
let(:comp) { Vector2d.new(200, 150) }
|
|
38
|
-
|
|
39
|
-
its(:x) { is_expected.to eq(150) }
|
|
40
|
-
its(:y) { is_expected.to eq(150) }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context "when scaling by width" do
|
|
44
|
-
let(:comp) { Vector2d.new(150, 200) }
|
|
45
|
-
|
|
46
|
-
its(:x) { is_expected.to eq(150) }
|
|
47
|
-
its(:y) { is_expected.to eq(150) }
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
describe "#fit_either" do
|
|
52
|
-
subject(:vector) { original.fit_either(comp) }
|
|
53
|
-
|
|
54
|
-
let(:original) { Vector2d.new(300, 300) }
|
|
55
|
-
|
|
56
|
-
context "when width is largest" do
|
|
57
|
-
let(:comp) { Vector2d.new(200, 150) }
|
|
58
|
-
|
|
59
|
-
its(:x) { is_expected.to eq(200) }
|
|
60
|
-
its(:y) { is_expected.to eq(200) }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
context "when height is largest" do
|
|
64
|
-
let(:comp) { Vector2d.new(150, 200) }
|
|
65
|
-
|
|
66
|
-
its(:x) { is_expected.to eq(200) }
|
|
67
|
-
its(:y) { is_expected.to eq(200) }
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Vector2d::Properties do
|
|
6
|
-
subject(:vector) { Vector2d.new(2, 3) }
|
|
7
|
-
|
|
8
|
-
describe "#angle" do
|
|
9
|
-
it "returns the angle" do
|
|
10
|
-
expect(vector.angle).to be_within(0.0001).of(0.9827)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
describe "#aspect_ratio" do
|
|
15
|
-
it "returns the aspect_ratio" do
|
|
16
|
-
expect(vector.aspect_ratio).to be_within(0.0001).of(0.6667)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe "#length" do
|
|
21
|
-
it "calculates the length" do
|
|
22
|
-
expect(vector.length).to be_within(0.0001).of(3.6055)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
describe "#squared_length" do
|
|
27
|
-
it "calculates the squared length" do
|
|
28
|
-
expect(vector.squared_length).to eq(13)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
describe "#normalized?" do
|
|
33
|
-
subject { vector.normalized? }
|
|
34
|
-
|
|
35
|
-
context "when vector is normalized" do
|
|
36
|
-
let(:vector) { Vector2d.new(2, 3).normalize }
|
|
37
|
-
|
|
38
|
-
it { is_expected.to be(true) }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
context "when vector isn't normalized" do
|
|
42
|
-
let(:vector) { Vector2d.new(2, 3) }
|
|
43
|
-
|
|
44
|
-
it { is_expected.to be(false) }
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Vector2d::Transformations do
|
|
6
|
-
subject(:vector) { Vector2d.new(2, 3) }
|
|
7
|
-
|
|
8
|
-
describe "#ceil" do
|
|
9
|
-
subject(:vector) { Vector2d.new(2.3, 3.3) }
|
|
10
|
-
|
|
11
|
-
it "rounds the vector up" do
|
|
12
|
-
expect(vector.ceil).to eq(Vector2d.new(3, 4))
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
describe "#clamp" do
|
|
17
|
-
subject(:vector) { Vector2d.new(2, 8) }
|
|
18
|
-
|
|
19
|
-
it "clamps each axis" do
|
|
20
|
-
expect(vector.clamp(Vector2d.new(3, 3), Vector2d.new(6, 6)))
|
|
21
|
-
.to eq(Vector2d.new(3, 6))
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it "coerces the bounds" do
|
|
25
|
-
expect(vector.clamp(3, 6)).to eq(Vector2d.new(3, 6))
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "leaves a vector within the bounds alone" do
|
|
29
|
-
expect(vector.clamp(0, 10)).to eq(vector)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe "#floor" do
|
|
34
|
-
subject(:vector) { Vector2d.new(2.7, 3.6) }
|
|
35
|
-
|
|
36
|
-
it "rounds the vector down" do
|
|
37
|
-
expect(vector.floor).to eq(Vector2d.new(2, 3))
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
describe "#normalize" do
|
|
42
|
-
subject { vector.normalize }
|
|
43
|
-
|
|
44
|
-
its(:x) { is_expected.to be_within(0.0001).of(0.5547) }
|
|
45
|
-
its(:y) { is_expected.to be_within(0.0001).of(0.8320) }
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe "#perpendicular" do
|
|
49
|
-
it "returns a perpendicular vector" do
|
|
50
|
-
expect(vector.perpendicular).to eq(Vector2d.new(-3, 2))
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
describe "#resize" do
|
|
55
|
-
subject(:resized) { vector.resize(2.0) }
|
|
56
|
-
|
|
57
|
-
it "modifies the vector length" do
|
|
58
|
-
expect(resized.length).to be_within(0.0001).of(2.0)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "modifies the x property" do
|
|
62
|
-
expect(resized.x).to be_within(0.0001).of(1.1094)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "modifies the y property" do
|
|
66
|
-
expect(resized.y).to be_within(0.0001).of(1.6641)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
describe "#reverse" do
|
|
71
|
-
it "reverses the vector" do
|
|
72
|
-
expect(vector.reverse).to eq(Vector2d.new(-2, -3))
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe "#rotate" do
|
|
77
|
-
subject { vector.rotate(rotation).round(3) }
|
|
78
|
-
|
|
79
|
-
let(:vector) { Vector2d.new(1, 0) }
|
|
80
|
-
|
|
81
|
-
context "when roating by PI" do
|
|
82
|
-
let(:rotation) { Math::PI }
|
|
83
|
-
|
|
84
|
-
it { is_expected.to eq(Vector2d.new(-1, 0)) }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
context "when roating by PI/2" do
|
|
88
|
-
let(:rotation) { Math::PI / 2 }
|
|
89
|
-
|
|
90
|
-
it { is_expected.to eq(Vector2d.new(0, 1)) }
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
context "when roating by -PI/2" do
|
|
94
|
-
let(:rotation) { -Math::PI / 2 }
|
|
95
|
-
|
|
96
|
-
it { is_expected.to eq(Vector2d.new(0, -1)) }
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
context "when roating by PI/4" do
|
|
100
|
-
let(:rotation) { Math::PI / 4 }
|
|
101
|
-
|
|
102
|
-
it { is_expected.to eq(Vector2d.new(0.707, 0.707)) }
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
describe "#round" do
|
|
107
|
-
let(:vector) { Vector2d.new(2.3333, 3.666) }
|
|
108
|
-
|
|
109
|
-
context "without argument" do
|
|
110
|
-
subject { vector.round }
|
|
111
|
-
|
|
112
|
-
it { is_expected.to eq(Vector2d.new(2, 4)) }
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
context "with precision" do
|
|
116
|
-
subject { vector.round(2) }
|
|
117
|
-
|
|
118
|
-
it { is_expected.to eq(Vector2d.new(2.33, 3.67)) }
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
describe "#truncate" do
|
|
123
|
-
context "when argument is longer than length" do
|
|
124
|
-
let(:arg) { 5.0 }
|
|
125
|
-
|
|
126
|
-
it "does not change the length" do
|
|
127
|
-
expect(vector.truncate(arg).length).to be_within(0.0001).of(3.6055)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
context "when argument is shorter than length" do
|
|
132
|
-
let(:arg) { 2.5 }
|
|
133
|
-
|
|
134
|
-
it "changes the length" do
|
|
135
|
-
expect(vector.truncate(arg).length).to be_within(0.0001).of(arg)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|