vector2d 1.1.2 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 682557f0f29ae91579f654ea0d158b294f34842a
4
- data.tar.gz: 3c06461f5a55f84d40c2d9a6ccaeb2c588180b5e
3
+ metadata.gz: 1406edfe102d42ed1f904b284c2d60a3fb7ab377
4
+ data.tar.gz: ad834281b90b577cb6b9d5e392cd27d8aed630cf
5
5
  SHA512:
6
- metadata.gz: be219090e10f35d43eee02aafb26008dac0763cc206d748e51169eff6f5d208a8afbb058e489f150584828ee2b58abb665595514bec5562d12fbe2f5ae69ed6a
7
- data.tar.gz: 46aba4ea456801fe4cf7403a431ef3a754424623004faec67b7c9290042af806dbd8195c6c67bcd70d9f01560228793a8938a5e9474e1c292eb35dd99946551f
6
+ metadata.gz: f5894c2e97725decc4e019eef6fa43b597ab4e5349e09fd036d516763d687eac3d9d9c882cfc785ff6cfcf1f9f5cf1d50a73eb4f7afb3e84f24619cdd23f79bd
7
+ data.tar.gz: 82bb98f6236e474dffc947c5ea8a95c52bb94d92c0e885cf95886129242b7e13fa4947042571ee3bc362b0414570c7ba95c8268971e3fdf21c8caa7d1e9172d0
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.2
6
+ script:
7
+ - bundle exec rspec spec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vector2d (1.1.2)
4
+ vector2d (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006 Inge Jørgensen
1
+ Copyright (c) 2006-2014 Inge Jørgensen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Vector2d [![Build Status](https://travis-ci.org/elektronaut/vector2d.png)](https://travis-ci.org/elektronaut/vector2d) [![Code Climate](https://codeclimate.com/github/elektronaut/vector2d.png)](https://codeclimate.com/github/elektronaut/vector2d)
2
+
3
+ Vector2d handles two-dimensional coordinates and vectors.
4
+ Vectors are immutable, meaning this is a purely functional library.
5
+
6
+ ## Quick example
7
+
8
+ require 'vector2d'
9
+
10
+ vector = Vector2d(50, 70)
11
+
12
+ vector.aspect_ratio # => 0.714285714285714
13
+ vector.length # => 86.0232526704263
14
+
15
+ vector * 2 # => Vector2d(140,100)
16
+ vector + Vector2d(20, 30) # => Vector2d(100,70)
17
+
18
+ vector.fit(Vector(64, 64)) # => Vector2d(64,45)
19
+
20
+ Vector2d.parse([50, 70]) # => Vector2d(50,70)
21
+ Vector2d.parse("50x70") # => Vector2d(50,70)
22
+
23
+ ## Documentation
24
+
25
+ [API documentation](http://rdoc.info/github/elektronaut/vector2d)
26
+
27
+ ## License
28
+
29
+ Vector2d is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/lib/vector2d.rb CHANGED
@@ -1,238 +1,92 @@
1
1
  # encoding: utf-8
2
2
 
3
- # Vector2d allows for easy handling of two-dimensionals coordinates and vectors.
4
- # It's very flexible, most methods accepts arguments as strings, arrays, hashes
5
- # or Vector2d objects.
6
- class Vector2d
7
-
8
- # X axis
9
- attr_accessor :x
10
- # Y axis
11
- attr_accessor :y
3
+ require 'vector2d/calculations'
4
+ require 'vector2d/coercions'
5
+ require 'vector2d/fitting'
6
+ require 'vector2d/properties'
7
+ require 'vector2d/transformations'
8
+ require 'vector2d/version'
12
9
 
13
- # Creates a new vector.
14
- # The following examples are all valid:
15
- #
16
- # Vector2d.new(150, 100)
17
- # Vector2d.new(150.0, 100.0)
18
- # Vector2d.new("150x100")
19
- # Vector2d.new("150.0x100.0")
20
- # Vector2d.new([150,100})
21
- # Vector2d.new({:x => 150, :y => 100})
22
- # Vector2d.new({"x" => 150.0, "y" => 100.0})
23
- # Vector2d.new(Vector2d.new(150, 100))
24
- def initialize(*args)
25
- args.flatten!
26
- if (args.length == 1)
27
- if (args[0].kind_of?(String) && args[0].match(/^[\s]*[\d\.]*[\s]*x[\s]*[\d\.]*[\s]*$/))
28
- args = args[0].split("x")
29
- elsif args[0].kind_of?(Vector2d)
30
- args = [args[0].x, args[0].y]
31
- elsif args[0].kind_of?(Hash)
32
- if args[0].has_key?(:y)
33
- args[1] = args[0][:y]
34
- elsif args[0].has_key?("y")
35
- args[1] = args[0]["y"]
36
- end
37
- if args[0].has_key?(:x)
38
- args[0] = args[0][:x]
39
- elsif args[0].has_key?("x")
40
- args[0] = args[0]["x"]
41
- end
10
+ class Vector2d
11
+ extend Vector2d::Calculations::ClassMethods
12
+ include Vector2d::Calculations
13
+ include Vector2d::Coercions
14
+ include Vector2d::Fitting
15
+ include Vector2d::Properties
16
+ include Vector2d::Transformations
17
+
18
+ class << self
19
+ # Creates a new vector.
20
+ # The following examples are all valid:
21
+ #
22
+ # Vector2d.parse(150, 100)
23
+ # Vector2d.parse(150.0, 100.0)
24
+ # Vector2d.parse("150x100")
25
+ # Vector2d.parse("150.0x100.0")
26
+ # Vector2d.parse([150,100})
27
+ # Vector2d.parse({x: 150, y: 100})
28
+ # Vector2d.parse({"x" => 150.0, "y" => 100.0})
29
+ # Vector2d.parse(Vector2d(150, 100))
30
+ def parse(arg, second_arg=nil)
31
+ if second_arg.nil?
32
+ parse_single_arg(arg)
42
33
  else
43
- args = [args[0], args[0]]
34
+ self.new(arg, second_arg)
44
35
  end
45
36
  end
46
- @x, @y = args[0].to_f, args[1].to_f
47
- end
48
-
49
- # Compares two vectors.
50
- def ==(comp)
51
- comp.x == @x && comp.y == y
52
- end
53
-
54
- # Returns a string representation of the vector.
55
- #
56
- # Example:
57
- # Vector2d.new( 150, 100 ).to_s # "150x100"
58
- def to_s
59
- "#{@x}x#{@y}"
60
- end
61
-
62
- # Length of vector
63
- def length
64
- Math.sqrt(length_squared)
65
- end
66
-
67
- # Returns the length of this vector before square root. Allows for a faster check.
68
- def length_squared
69
- @x * @x + @y * @y
70
- end
71
-
72
- # Set new length.
73
- def length=(new_length)
74
- v = self * (new_length/length)
75
- @x, @y = v.x, v.y
76
- end
77
-
78
- # Returns a normalized (length = 1.0) version of the vector.
79
- def normalize
80
- self.dup.normalize!
81
- end
82
-
83
- # In-place form of Vector2d.normalize.
84
- def normalize!
85
- self.length = 1.0
86
- self
87
- end
88
-
89
- def normalized?
90
- self.length == 1.0
91
- end
92
-
93
- # Rounds coordinates to nearest integer.
94
- def round
95
- self.dup.round!
96
- end
97
-
98
- # In-place form of Vector2d.round.
99
- def round!
100
- @x, @y = @x.round, @y.round
101
- self
102
- end
103
37
 
104
- # Returns the aspect ratio of the vector.
105
- def aspect_ratio
106
- (@x/@y).abs
107
- end
108
-
109
- # Multiply vectors. If args is a single Numeric, both axis will be multiplied.
110
- def *(*vector_or_number)
111
- v = Vector2d::new(vector_or_number)
112
- Vector2d.new(@x*v.x, @y*v.y)
113
- end
114
-
115
- # Divide vectors. If args is a single Numeric, both axis will be divided.
116
- def /(*vector_or_number)
117
- v = Vector2d::new(vector_or_number)
118
- Vector2d.new(@x/v.x, @y/v.y)
119
- end
120
-
121
- # Add vectors. If args is a single Numeric, it will be added to both axis.
122
- def +(*vector_or_number)
123
- v = Vector2d::new(vector_or_number)
124
- Vector2d.new(@x+v.x, @y+v.y)
125
- end
126
-
127
- # Subtract vectors. If args is a single Numeric, it will be subtracted from both axis.
128
- def -(*vector_or_number)
129
- v = Vector2d::new(vector_or_number)
130
- Vector2d.new(@x-v.x, @y-v.y)
131
- end
132
-
133
- # return a new vector perpendicular to this one
134
- def perpendicular
135
- Vector2d.new(-@y, @x)
136
- end
137
-
138
- # distance between two vectors
139
- def distance(vector2)
140
- Math.sqrt(distance_sq(vector2))
141
- end
142
-
143
- # Calculate squared distance between vectors. Faster than standard distance.
144
- # param vector2 The other vector.
145
- # returns the squared distance between the vectors.
146
- def distance_sq(vector2)
147
- dx = vector2.x - @x
148
- dy = vector2.y - @y
149
- dx * dx + dy * dy
150
- end
151
-
152
- # angle of this vector
153
- def angle
154
- Math.atan2(@y, @x)
155
- end
156
-
157
- # sets the length under the given value. Nothing is done if
158
- # the vector is already shorter.
159
- def truncate(max)
160
- self.length = [max, self.length].min
161
- self
162
- end
163
-
164
- # Makes the vector face the opposite way.
165
- def reverse
166
- @x = -@x
167
- @y = -@y
168
- self
169
- end
170
-
171
- # dot product of this vector and another vector
172
- def dot_product(vector2)
173
- self.class.dot_product(self, vector2)
174
- end
175
-
176
- # cross product of this vector and another vector
177
- def cross_product(vector2)
178
- self.class.cross_product(self, vector2)
179
- end
180
-
181
- # angle in radians between this vector and another
182
- def angle_between(vector2)
183
- self.class.angle_between(self, vector2)
184
- end
38
+ private
39
+
40
+ def parse_single_arg(arg)
41
+ case arg
42
+ when Vector2d
43
+ arg
44
+ when Array
45
+ self.parse(*arg)
46
+ when String
47
+ parse_str(arg)
48
+ when Hash
49
+ parse_hash(arg.dup)
50
+ else
51
+ self.new(arg, arg)
52
+ end
53
+ end
185
54
 
186
- # cross product of two vectors
187
- def self.cross_product(vector1, vector2)
188
- vector1.x * vector2.y - vector1.y * vector2.x
189
- end
55
+ def parse_hash(hash)
56
+ hash[:x] ||= hash['x'] if hash.has_key?('x')
57
+ hash[:y] ||= hash['y'] if hash.has_key?('y')
58
+ self.new(hash[:x], hash[:y])
59
+ end
190
60
 
191
- # dot product of two vectors
192
- def self.dot_product(vector1, vector2)
193
- vector1.x * vector2.x + vector1.y * vector2.y
61
+ def parse_str(str)
62
+ if str =~ /^[\s]*[\d\.]*[\s]*x[\s]*[\d\.]*[\s]*$/
63
+ self.new(*str.split("x").map(&:to_f))
64
+ else
65
+ raise ArgumentError, "not a valid string input"
66
+ end
67
+ end
194
68
  end
195
69
 
196
- # angle between two vectors in radians
197
- def self.angle_between(vector1, vector2)
198
- one = vector1.normalized? ? vector1 : vector1.normalize
199
- two = vector2.normalized? ? vector2 : vector2.normalize
200
- Math.acos(self.dot_product(one, two))
201
- end
70
+ attr_reader :x, :y
202
71
 
203
- # Constrain/expand so that both coordinates fit within (the square implied by) another vector.
204
- # This is useful for resizing images to fit a certain size while keeping aspect ratio.
205
- #
206
- # == Examples
207
- #
208
- # my_image = Vector2d.new("320x200") # Creates a new vector object
209
- # my_image.constrain_both(100) # Returns a new vector: x=100, y=63
210
- # my_image.constrain_both(150, 50) # Returns a new vector: x=80, y=50
211
- # my_image.constrain_both("150x50") # Equal to constrain_both( 150, 50 )
212
- # my_image.constrain_both("x100") # Returns a new vector: x=160, y=100
213
- #
214
- # == Note
215
- #
216
- # Either axis will be disregarded if zero or nil (see the last example). This is a feature, not a bug. ;)
217
- def constrain_both(*vector_or_number)
218
- scale = Vector2d::new(vector_or_number) / self
219
- (self * ((scale.y==0||(scale.x>0&&scale.x<scale.y)) ? scale.x : scale.y))
72
+ def initialize(x, y)
73
+ @x, @y = x, y
220
74
  end
221
75
 
222
- # Constrain/expand so that one of the coordinates fit within (the square implied by) another vector.
76
+ # Compares two vectors
223
77
  #
224
- # == Example
78
+ # Vector2d(2, 3) == Vector2d(2, 3) # => true
79
+ # Vector2d(2, 3) == Vector2d(1, 0) # => false
225
80
  #
226
- # my_image = Vector2d.new("320x200") # Creates a new vector object
227
- # my_image.constrain_one(100, 100) # Returns a new vector: x=160, y=100
228
- def constrain_one( *vector_or_number )
229
- scale = Vector2d::new(vector_or_number) / self
230
- if (scale.x > 0 && scale.y > 0)
231
- scale = (scale.x<scale.y) ? scale.y : scale.x
232
- self * scale
233
- else
234
- constrain_both(vector_or_number)
235
- end
81
+ def ==(comp)
82
+ comp.x === x && comp.y === y
236
83
  end
237
-
238
84
  end
85
+
86
+ # Instantiates a Vector2d
87
+ #
88
+ # Vector2d(2, 3) # => Vector2d(2,3)
89
+ #
90
+ def Vector2d(*args)
91
+ Vector2d.parse(*args)
92
+ end
@@ -0,0 +1,141 @@
1
+ # encoding: utf-8
2
+
3
+ class Vector2d
4
+ module Calculations
5
+ module ClassMethods
6
+ # Calculates cross product of two vectors.
7
+ #
8
+ # v1 = Vector2d(2, 1)
9
+ # v2 = Vector2d(2, 3)
10
+ # Vector2d.cross_product(v1, v2) # => 4
11
+ #
12
+ def cross_product(vector1, vector2)
13
+ vector1.x * vector2.y - vector1.y * vector2.x
14
+ end
15
+
16
+ # Calculates dot product of two vectors.
17
+ #
18
+ # v1 = Vector2d(2, 1)
19
+ # v2 = Vector2d(2, 3)
20
+ # Vector2d.dot_product(v1, v2) # => 10
21
+ #
22
+ def dot_product(vector1, vector2)
23
+ vector1.x * vector2.x + vector1.y * vector2.y
24
+ end
25
+
26
+ # Calculates angle between two vectors in radians.
27
+ #
28
+ # v1 = Vector2d(2, 3)
29
+ # v2 = Vector2d(4, 5)
30
+ # Vector2d.angle_between(v1, v2) # => 0.0867..
31
+ #
32
+ def angle_between(vector1, vector2)
33
+ one = vector1.normalized? ? vector1 : vector1.normalize
34
+ two = vector2.normalized? ? vector2 : vector2.normalize
35
+ Math.acos(self.dot_product(one, two))
36
+ end
37
+ end
38
+
39
+ # Multiplies vectors.
40
+ #
41
+ # Vector2d(1, 2) * Vector2d(2, 3) # => Vector2d(2, 6)
42
+ # Vector2d(1, 2) * 2 # => Vector2d(2, 4)
43
+ #
44
+ def *(other)
45
+ calculate_each(:*, other)
46
+ end
47
+
48
+ # Divides vectors.
49
+ #
50
+ # Vector2d(4, 2) / Vector2d(2, 1) # => Vector2d(2, 2)
51
+ # Vector2d(4, 2) / 2 # => Vector2d(2, 1)
52
+ #
53
+ def /(other)
54
+ calculate_each(:/, other)
55
+ end
56
+
57
+ # Adds vectors.
58
+ #
59
+ # Vector2d(1, 2) + Vector2d(2, 3) # => Vector2d(3, 5)
60
+ # Vector2d(1, 2) + 2 # => Vector2d(3, 4)
61
+ #
62
+ def +(other)
63
+ calculate_each(:+, other)
64
+ end
65
+
66
+ # Subtracts vectors.
67
+ #
68
+ # Vector2d(2, 3) - Vector2d(2, 1) # => Vector2d(0, 2)
69
+ # Vector2d(4, 3) - 1 # => Vector2d(3, 2)
70
+ #
71
+ def -(other)
72
+ calculate_each(:-, other)
73
+ end
74
+
75
+ # Calculates the distance between two vectors.
76
+ #
77
+ # v1 = Vector2d(2, 3)
78
+ # v2 = Vector2d(5, 6)
79
+ # v1.distance(v2) # => 1.4142..
80
+ #
81
+ def distance(other)
82
+ Math.sqrt(squared_distance(other))
83
+ end
84
+
85
+ # Calculate squared distance between vectors.
86
+ #
87
+ # v1 = Vector2d(2, 3)
88
+ # v2 = Vector2d(5, 6)
89
+ # v1.distance_squared(v2) # => 18
90
+ #
91
+ def squared_distance(other)
92
+ v, _ = coerce(other)
93
+ dx = v.x - x
94
+ dy = v.y - y
95
+ dx * dx + dy * dy
96
+ end
97
+
98
+ # Dot product of this vector and another vector.
99
+ #
100
+ # v1 = Vector2d(2, 1)
101
+ # v2 = Vector2d(2, 3)
102
+ # v1.dot_product(v2) # => 10
103
+ #
104
+ def dot_product(other)
105
+ v, _ = coerce(other)
106
+ self.class.dot_product(self, v)
107
+ end
108
+
109
+ # Cross product of this vector and another vector.
110
+ #
111
+ # v1 = Vector2d(2, 1)
112
+ # v2 = Vector2d(2, 3)
113
+ # v1.cross_product(v2) # => 4
114
+ #
115
+ def cross_product(other)
116
+ v, _ = coerce(other)
117
+ self.class.cross_product(self, v)
118
+ end
119
+
120
+ # Angle in radians between this vector and another vector.
121
+ #
122
+ # v1 = Vector2d(2, 3)
123
+ # v2 = Vector2d(4, 5)
124
+ # v1.angle_between(v2) # => 0.0867..
125
+ #
126
+ def angle_between(other)
127
+ v, _ = coerce(other)
128
+ self.class.angle_between(self, v)
129
+ end
130
+
131
+ private
132
+
133
+ def calculate_each(method, other)
134
+ v, _ = coerce(other)
135
+ self.class.new(
136
+ x.send(method, v.x),
137
+ y.send(method, v.y)
138
+ )
139
+ end
140
+ end
141
+ end