Module: Vector2d::Angles

Included in:
Vector2d
Defined in:
lib/vector2d/angles.rb

Overview

The angle API. Radians are the native unit: every angle is measured counterclockwise from the positive x axis, and every method takes and returns radians unless its name says degrees.

The degree methods are conversions layered on top, not a second angle API. Two general converters, .radians and .degrees, do the arithmetic once. Three degree variants build on them, each one the radian method with the angle converted: .from_degrees is .from_angle, #angle_in_degrees is #angle, and #rotate_degrees is #rotate. The rest of the angle API stays radians only. Convert at the call site for those.

Vector2d(2, 3).rotate_around(Vector2d(1, 1), Vector2d.radians(90))
# => Vector2d(-1.0,2.0)

Vector2d.degrees(Vector2d(2, 3).angle_to(Vector2d(4, 5)))
# => -4.9697..

Porting note: pygame's rotate() and Unity's Vector2.Angle are in degrees, so those angles need .radians on the way in. Godot and Rust's glam are radians like this library, and their angles carry over unconverted.

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#angleFloat

Angle of vector.

Vector2d(2, 3).angle # => 0.9827..

Returns:

  • (Float)

    the angle in radians



159
160
161
# File 'lib/vector2d/angles.rb', line 159

def angle
  Math.atan2(y, x)
end

#angle_between(other) ⇒ Float Also known as: angle_with

Unsigned angle in radians between this vector and another vector, in the range 0..PI. This is the magnitude of #angle_to, so it is the same in either direction.

v1 = Vector2d(2, 3)
v2 = Vector2d(4, 5)
v1.angle_between(v2) # => 0.0867..
v2.angle_between(v1) # => 0.0867..

Only the directions matter, not the magnitudes. The zero vector has no direction, and the angle between it and anything is zero.

v1.angle_between(Vector2d(0, 0)) # => 0.0

Parameters:

  • other (Vector2d, Array, String, Hash, Integer, Float, Rational, BigDecimal, ::Vector, ::Matrix)

    anything Vector2d.parse accepts

Returns:

  • (Float)

    the angle in radians, in 0..PI



200
201
202
# File 'lib/vector2d/angles.rb', line 200

def angle_between(other)
  angle_to(other).abs
end

#angle_in_degreesFloat

Angle of the vector in degrees. This is #angle put through .degrees, and follows the same convention: angles are measured counterclockwise from the positive x axis, in the range -180..180.

Vector2d(1, 0).angle_in_degrees  # => 0.0
Vector2d(0, 1).angle_in_degrees  # => 90.0
Vector2d(2, 3).angle_in_degrees  # => 56.3099..
Vector2d(0, -1).angle_in_degrees # => -90.0

Vector2d.from_degrees takes the angle back.

Vector2d.from_degrees(Vector2d(2, 3).angle_in_degrees, 5.0)
# => Vector2d(2.7735..,4.1602..)

Returns:

  • (Float)

    the angle in degrees, in -180..180



289
290
291
# File 'lib/vector2d/angles.rb', line 289

def angle_in_degrees
  self.class.degrees(angle)
end

#angle_to(other) ⇒ Float

Signed angle in radians from this vector to another vector, in the range -PI..PI. The angle is positive when the other vector is counterclockwise from this one.

v1 = Vector2d(2, 3)
v2 = Vector2d(4, 5)
v1.angle_to(v2) # => -0.0867..
v2.angle_to(v1) # => 0.0867..

Only the directions matter, not the magnitudes. The zero vector has no direction, and the angle to or from it is zero.

v1.angle_to(Vector2d(0, 0)) # => 0.0

Parameters:

  • other (Vector2d, Array, String, Hash, Integer, Float, Rational, BigDecimal, ::Vector, ::Matrix)

    anything Vector2d.parse accepts

Returns:

  • (Float)

    the angle in radians, in -PI..PI



179
180
181
182
# File 'lib/vector2d/angles.rb', line 179

def angle_to(other)
  v = coerce_vector(other)
  self.class.angle_to(self, v)
end

#perpendicularself

Returns the vector rotated a quarter turn counterclockwise.

Vector2d(2, 3).perpendicular # => Vector2d(-3,2)

Counterclockwise is the same positive direction #rotate turns in. Use #perpendicular_cw for the other one.

Returns:

  • (self)


260
261
262
# File 'lib/vector2d/angles.rb', line 260

def perpendicular
  build(-y, x)
end

#perpendicular_cwself

Returns the vector rotated a quarter turn clockwise.

Vector2d(2, 3).perpendicular_cw # => Vector2d(3,-2)

Returns:

  • (self)


269
270
271
# File 'lib/vector2d/angles.rb', line 269

def perpendicular_cw
  build(y, -x)
end

#rotate(angle) ⇒ self

Rotates the vector around the origin. The angle is in radians, and a positive angle turns counterclockwise.

Vector2d(2, 3).rotate(Math::PI / 2) # => Vector2d(-3.0,2.0)

Raises ArgumentError unless the angle is a real number.

Vector2d(2, 3).rotate(Complex(1, 2)) # => ArgumentError

Parameters:

  • angle (Integer, Float, Rational, BigDecimal)

    the angle in radians

Returns:

  • (self)


230
231
232
233
234
235
# File 'lib/vector2d/angles.rb', line 230

def rotate(angle)
  angle = coordinate(angle)
  cos = Math.cos(angle)
  sin = Math.sin(angle)
  build((x * cos) - (y * sin), (x * sin) + (y * cos))
end

#rotate_around(center, angle) ⇒ self

Rotates the vector around another point. The center is coerced, so scalars work too. The angle is in radians, and a positive angle turns counterclockwise.

Vector2d(2, 1).rotate_around(Vector2d(1, 1), Math::PI / 2)
# => Vector2d(1.0,2.0)

Parameters:

  • center (Vector2d, Array, String, Hash, Integer, Float, Rational, BigDecimal, ::Vector, ::Matrix)

    anything Vector2d.parse accepts

  • angle (Integer, Float, Rational, BigDecimal)

    the angle in radians

Returns:

  • (self)


247
248
249
250
# File 'lib/vector2d/angles.rb', line 247

def rotate_around(center, angle)
  center_v = coerce_vector(center)
  (self - center_v).rotate(angle) + center_v
end

#rotate_degrees(angle) ⇒ self

Rotates the vector around the origin by an angle in degrees. This is #rotate with the angle put through .radians, and turns the same way: a positive angle turns counterclockwise.

Vector2d(2, 3).rotate_degrees(90) # => Vector2d(-3.0,2.0)

The two agree exactly wherever .radians lands on the angle #rotate would have been given.

Vector2d(2, 3).rotate_degrees(90) == Vector2d(2, 3).rotate(Math::PI / 2)
# => true

Raises ArgumentError unless the angle is a real number.

Vector2d(2, 3).rotate_degrees(Complex(1, 2)) # => ArgumentError

Parameters:

  • angle (Integer, Float, Rational, BigDecimal)

    the angle in degrees

Returns:

  • (self)


311
312
313
# File 'lib/vector2d/angles.rb', line 311

def rotate_degrees(angle)
  rotate(self.class.radians(angle))
end

#to_polarArray(Float, Float)

Polar coordinates of vector, as a [length, angle] array.

Vector2d(2, 3).to_polar # => [3.6055.., 0.9827..]

Vector2d.from_angle takes the same pair back.

length, angle = Vector2d(2, 3).to_polar
Vector2d.from_angle(angle, length) # => Vector2d(2.0,3.0)

Returns:

  • (Array(Float, Float))

    the length and the angle in radians



215
216
217
# File 'lib/vector2d/angles.rb', line 215

def to_polar
  [length, angle]
end