vector2d 1.1.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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