Module: Vector2d::Dimensions

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

Overview

The vector as a rectangle. Vector2d grew out of handling image dimensions, and these methods describe a size and fit one inside another. All of them compare coordinates by magnitude, so a negative vector describes the same rectangle as its positive twin.

Instance Method Summary collapse

Instance Method Details

#areaInteger, ...

Area covered by the vector, the product of its coordinates. Coordinates are taken by magnitude, as in #aspect_ratio, so the area is never negative.

Vector2d(2, 3).area  # => 6
Vector2d(-2, 3).area # => 6

A vector without width or height covers nothing.

Vector2d(2, 0).area # => 0

Returns:

  • (Integer, Float, Rational, BigDecimal)


21
22
23
# File 'lib/vector2d/dimensions.rb', line 21

def area
  (x * y).abs
end

#aspect_ratioFloat

Aspect ratio of vector.

Vector2d(2, 3).aspect_ratio # => 0.6666..

A vector without height has no aspect ratio, so ArgumentError is raised.

Vector2d(2, 0).aspect_ratio # => ArgumentError
Vector2d(0, 0).aspect_ratio # => ArgumentError

Returns:

  • (Float)

Raises:

  • (ArgumentError)


36
37
38
39
40
41
# File 'lib/vector2d/dimensions.rb', line 36

def aspect_ratio
  raise ArgumentError, "the zero vector has no aspect ratio" if zero?
  raise ArgumentError, "#{inspect} has no aspect ratio, y is zero" if y.zero?

  (x.to_f / y).abs
end

#constrain_both(other) ⇒ self

Deprecated.

Use #fit instead.

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (self)


265
266
267
268
# File 'lib/vector2d/dimensions.rb', line 265

def constrain_both(other)
  warn_deprecated("Vector2d#constrain_both is deprecated. Use #fit instead.")
  fit(other)
end

#constrain_one(other) ⇒ self

Deprecated.

Use #cover instead.

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (self)


274
275
276
277
# File 'lib/vector2d/dimensions.rb', line 274

def constrain_one(other)
  warn_deprecated("Vector2d#constrain_one is deprecated. Use #cover instead.")
  cover(other)
end

#contain(other) ⇒ Vector2d

Deprecated.

Use other.fit(self, upscale: false) instead.

Scales down the given vector unless it fits inside.

vector = Vector2d(20, 20)
vector.contain(Vector2d(10, 10)) # => Vector2d(10,10)
vector.contain(Vector2d(40, 20)) # => Vector2d(20.0,10.0)
vector.contain(Vector2d(20, 40)) # => Vector2d(10.0,20.0)

Coordinates are compared by magnitude, so negative vectors are scaled the same way and keep their direction.

vector.contain(Vector2d(-40, 20)) # => Vector2d(-20.0,10.0)

An axis that is zero is unconstrained, as in #fit, so the zero vector contains anything.

Vector2d(0, 0).contain(Vector2d(40, 20)) # => Vector2d(40,20)

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (Vector2d)

    a vector of the argument's class, not of this one



255
256
257
258
259
# File 'lib/vector2d/dimensions.rb', line 255

def contain(other)
  warn_deprecated("Vector2d#contain is deprecated. " \
                  "Use `other.fit(self, upscale: false)` instead.")
  coerce_vector(other).fit(self, upscale: false)
end

#cover(other, upscale: true) ⇒ self

Scales the vector to cover another vector, retaining the aspect ratio. Where #fit scales until the vector is contained by the constraint, #cover scales until it contains the constraint.

constraint = Vector2d(5, 5)
Vector2d(20, 10).cover(constraint) # => Vector2d(10.0,5.0)
Vector2d(10, 20).cover(constraint) # => Vector2d(5.0,10.0)

Pass upscale: false to scale down only, leaving a vector that already covers the constraint unchanged.

Vector2d(20, 10).cover(Vector2d(40, 40), upscale: false)
# => Vector2d(20,10)

As in #fit, coordinates are constrained by magnitude and the vector keeps its direction.

Vector2d(-20, 10).cover(constraint) # => Vector2d(-10.0,5.0)

Note: Either axis will be disregarded if zero or nil, as in #fit. This is a feature, not a bug.

Vector2d(0, 10).cover(constraint) # => Vector2d(0.0,5.0)
Vector2d(20, 10).cover(Vector2d(0, 0)) # => Vector2d(20,10)

The zero vector has no direction, and is returned unchanged.

Vector2d(0, 0).cover(Vector2d(5, 5)) # => Vector2d(0,0)

Parameters:

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

    anything Vector2d.parse accepts

  • upscale (Boolean) (defaults to: true)

    whether to scale a vector that already covers up to the constraint

Returns:

  • (self)


195
196
197
# File 'lib/vector2d/dimensions.rb', line 195

def cover(other, upscale: true)
  scale_by(fit_factors(coerce_vector(other)).max, upscale: upscale)
end

#covers?(other) ⇒ Boolean

Does the vector already cover another vector? True whenever #cover would shrink the vector or leave it alone, rather than scaling it up. This is the predicate to #cover that #fits? is to #fit.

constraint = Vector2d(5, 5)
Vector2d(20, 10).covers?(constraint) # => true
Vector2d(20, 1).covers?(constraint)  # => false

Coordinates are compared by magnitude, as in #cover.

Vector2d(-20, 10).covers?(constraint) # => true

Note: Either axis will be disregarded if zero or nil, as in #cover, so a vector flat on one axis still counts as covering.

Vector2d(0, 10).covers?(constraint)  # => true
Vector2d(0, 0).covers?(constraint)   # => true

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (Boolean)


220
221
222
223
# File 'lib/vector2d/dimensions.rb', line 220

def covers?(other)
  factor = fit_factors(coerce_vector(other)).max
  factor.nil? || factor <= 1
end

#fit(other, upscale: true) ⇒ self

Scales the vector to fit inside another vector, retaining the aspect ratio.

vector = Vector2d(20, 10)
vector.fit(Vector2d(10, 10)) # => Vector2d(10.0,5.0)
vector.fit(Vector2d(20, 20)) # => Vector2d(20.0,10.0)
vector.fit(Vector2d(40, 40)) # => Vector2d(40.0,20.0)

Pass upscale: false to scale down only, leaving a vector that already fits unchanged.

vector.fit(Vector2d(40, 40), upscale: false) # => Vector2d(20,10)
vector.fit(Vector2d(10, 10), upscale: false) # => Vector2d(10.0,5.0)

The constraint applies to the magnitude of each coordinate, and the vector keeps its direction.

Vector2d(-20, 10).fit(Vector2d(5, 5)) # => Vector2d(-5.0,2.5)

Note: Either axis will be disregarded if zero or nil. This is a feature, not a bug. A constraint that is zero on both axes leaves the vector unchanged.

Vector2d(20, 10).fit(Vector2d(0, 0)) # => Vector2d(20,10)

The zero vector has no direction, and is returned unchanged.

Vector2d(0, 0).fit(Vector2d(10, 10)) # => Vector2d(0,0)

Parameters:

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

    anything Vector2d.parse accepts

  • upscale (Boolean) (defaults to: true)

    whether to scale a vector that already fits up to the constraint

Returns:

  • (self)


127
128
129
# File 'lib/vector2d/dimensions.rb', line 127

def fit(other, upscale: true)
  scale_by(fit_factors(coerce_vector(other)).min, upscale: upscale)
end

#fit_either(other) ⇒ self

Deprecated.

Use #cover instead.

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (self)


229
230
231
232
# File 'lib/vector2d/dimensions.rb', line 229

def fit_either(other)
  warn_deprecated("Vector2d#fit_either is deprecated. Use #cover instead.")
  cover(other)
end

#fits?(other) ⇒ Boolean

Does the vector already fit inside another vector? True whenever fit(other, upscale: false) would leave it unchanged.

constraint = Vector2d(20, 20)
Vector2d(20, 10).fits?(constraint) # => true
Vector2d(40, 10).fits?(constraint) # => false

A vector that exactly matches the constraint fits.

Vector2d(20, 20).fits?(constraint) # => true

Coordinates are compared by magnitude, as in #fit.

Vector2d(-20, 10).fits?(constraint) # => true

Note: Either axis will be disregarded if zero or nil, as in #fit. An axis that doesn't constrain can't be overflowed either.

Vector2d(40, 10).fits?(Vector2d(0, 20)) # => true

The zero vector has no size, and fits inside anything.

Vector2d(0, 0).fits?(constraint) # => true

Parameters:

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

    anything Vector2d.parse accepts

Returns:

  • (Boolean)


157
158
159
160
# File 'lib/vector2d/dimensions.rb', line 157

def fits?(other)
  factor = fit_factors(coerce_vector(other)).min
  factor.nil? || factor >= 1
end

#landscape?Boolean

Is the vector wider than it is tall?

Vector2d(3, 2).landscape? # => true
Vector2d(2, 3).landscape? # => false
Vector2d(2, 2).landscape? # => false

Coordinates are compared by magnitude, as in #aspect_ratio.

Vector2d(-3, 2).landscape? # => true

Returns:

  • (Boolean)


54
55
56
# File 'lib/vector2d/dimensions.rb', line 54

def landscape?
  x.abs > y.abs
end

#portrait?Boolean

Is the vector taller than it is wide?

Vector2d(2, 3).portrait? # => true
Vector2d(3, 2).portrait? # => false
Vector2d(2, 2).portrait? # => false

Coordinates are compared by magnitude, as in #aspect_ratio.

Vector2d(2, -3).portrait? # => true

Returns:

  • (Boolean)


69
70
71
# File 'lib/vector2d/dimensions.rb', line 69

def portrait?
  x.abs < y.abs
end

#square?Boolean

Is the vector as wide as it is tall?

Vector2d(2, 2).square? # => true
Vector2d(2, 3).square? # => false

Coordinates are compared by magnitude, as in #aspect_ratio. Exactly one of #square?, #landscape? and #portrait? holds for any vector.

Vector2d(-2, 2).square? # => true

Unlike #aspect_ratio, these three don't single out the zero vector. It is square.

Vector2d(0, 0).square? # => true

Returns:

  • (Boolean)


90
91
92
# File 'lib/vector2d/dimensions.rb', line 90

def square?
  x.abs == y.abs
end