Module: Vector2d::Comparison
- Included in:
- Vector2d
- Defined in:
- lib/vector2d/comparison.rb
Overview
Predicates relating two vectors, and the checks on coordinates that arithmetic can take out of range. Where #== compares exactly, the predicates here allow for floating point drift.
Instance Method Summary collapse
-
#approx_equal?(other, tolerance = nil) ⇒ Boolean
Are the two vectors equal, give or take floating point drift? Arithmetic that should land on a given vector usually lands a few ulps off it instead, which #== reports as a difference.
-
#finite? ⇒ Boolean
Are both coordinates finite?.
-
#independent?(other) ⇒ Boolean
Are the two vectors linearly independent? This is the inverse of #parallel?, and matches Vector#independent? in the standard library.
-
#nan? ⇒ Boolean
Is either coordinate NaN? Nothing else in the library produces one, but arithmetic on infinities does.
-
#opposite?(other) ⇒ Boolean
Do the two vectors point in opposite directions? Only the directions matter, not the magnitudes.
-
#parallel?(other) ⇒ Boolean
Are the two vectors parallel? Vectors pointing in opposite directions are parallel too.
-
#perpendicular?(other) ⇒ Boolean
Are the two vectors perpendicular to each other?.
Instance Method Details
#approx_equal?(other, tolerance = nil) ⇒ Boolean
Are the two vectors equal, give or take floating point drift? Arithmetic that should land on a given vector usually lands a few ulps off it instead, which #== reports as a difference.
v1 = Vector2d(0.1, 0.2) + Vector2d(0.2, 0.4)
v2 = Vector2d(0.3, 0.6)
v1 # => Vector2d(0.30000000000000004,0.6000..)
v1 == v2 # => false
v1.approx_equal?(v2) # => true
The other vector is coerced, unlike in #==, so anything .parse accepts is compared as a vector.
v1.approx_equal?([0.3, 0.6]) # => true
v1 == [0.3, 0.6] # => false
The default tolerance is the one #parallel? and #perpendicular? use, a few ulps scaled by the magnitude of the vectors, so the same amount of drift is absorbed whatever the coordinates are sized like. It covers rounding error, and nothing more.
big = Vector2d(1e8, 2e8)
big.approx_equal?(big.rotate(2 * Math::PI)) # => true
big.approx_equal?(Vector2d(1e8, 2.1e8)) # => false
Pass a tolerance for anything looser. It is an absolute distance between the two vectors, and is not scaled.
v3 = Vector2d(2, 3)
v3.approx_equal?(Vector2d(2, 4), 1.5) # => true
v3.approx_equal?(Vector2d(2, 4), 0.5) # => false
Note that this is not a replacement for #==. Approximate equality is not transitive, and vectors that are approximately equal do not have the same #hash.
48 49 50 51 52 53 |
# File 'lib/vector2d/comparison.rb', line 48 def approx_equal?(other, tolerance = nil) v = coerce_vector(other) return distance(v) <= coordinate(tolerance) unless tolerance.nil? near_zero?(distance(v), [1.0, length, v.length].max) end |
#finite? ⇒ Boolean
Are both coordinates finite?
Vector2d(2, 3).finite? # => true
Vector2d(2, Float::INFINITY).finite? # => false
Vector2d(2, Float::NAN).finite? # => false
153 154 155 |
# File 'lib/vector2d/comparison.rb', line 153 def finite? x.finite? && y.finite? end |
#independent?(other) ⇒ Boolean
Are the two vectors linearly independent? This is the inverse of #parallel?, and matches Vector#independent? in the standard library.
v = Vector2d(2, 3)
v.independent?(Vector2d(3, 2)) # => true
v.independent?(Vector2d(4, 6)) # => false
v.independent?(Vector2d(-4, -6)) # => false
The zero vector is parallel to everything, so nothing is independent of it.
v.independent?(Vector2d(0, 0)) # => false
93 94 95 |
# File 'lib/vector2d/comparison.rb', line 93 def independent?(other) !parallel?(other) end |
#nan? ⇒ Boolean
Is either coordinate NaN? Nothing else in the library produces one, but arithmetic on infinities does.
Vector2d(2, 3).nan? # => false
Vector2d(2, Float::NAN).nan? # => true
(Vector2d(2, 3) * Float::INFINITY * 0).nan? # => true
166 167 168 |
# File 'lib/vector2d/comparison.rb', line 166 def nan? coordinate_nan?(x) || coordinate_nan?(y) end |
#opposite?(other) ⇒ Boolean
Do the two vectors point in opposite directions? Only the directions matter, not the magnitudes.
v = Vector2d(2, 3)
v.opposite?(Vector2d(-2, -3)) # => true
v.opposite?(Vector2d(-4, -6)) # => true
v.opposite?(Vector2d(4, 6)) # => false
v.opposite?(Vector2d(3, 2)) # => false
Opposite vectors are parallel, but #parallel? does not care which way along the line the other vector points.
v.parallel?(Vector2d(-4, -6)) # => true
The zero vector has no direction to be the opposite of, so unlike #parallel? and #perpendicular?, which it satisfies trivially, it is opposite to nothing.
v.opposite?(Vector2d(0, 0)) # => false
119 120 121 122 123 124 |
# File 'lib/vector2d/comparison.rb', line 119 def opposite?(other) v = coerce_vector(other) return false if zero? || v.zero? parallel?(v) && dot_product(v).negative? end |
#parallel?(other) ⇒ Boolean
Are the two vectors parallel? Vectors pointing in opposite directions are parallel too.
v = Vector2d(2, 3)
v.parallel?(Vector2d(4, 6)) # => true
v.parallel?(Vector2d(-4, -6)) # => true
v.parallel?(Vector2d(3, 2)) # => false
Only the directions matter, not the magnitudes. The zero vector has no direction, and is parallel to everything.
v.parallel?(Vector2d(0, 0)) # => true
70 71 72 73 74 75 |
# File 'lib/vector2d/comparison.rb', line 70 def parallel?(other) v = coerce_vector(other) return true if zero? || v.zero? near_zero?(cross_product(v), length * v.length) end |
#perpendicular?(other) ⇒ Boolean
Are the two vectors perpendicular to each other?
v = Vector2d(2, 3)
v.perpendicular?(Vector2d(-3, 2)) # => true
v.perpendicular?(Vector2d(3, 2)) # => false
Only the directions matter, not the magnitudes. The zero vector has no direction, and is perpendicular to everything.
v.perpendicular?(Vector2d(0, 0)) # => true
139 140 141 142 143 144 |
# File 'lib/vector2d/comparison.rb', line 139 def perpendicular?(other) v = coerce_vector(other) return true if zero? || v.zero? near_zero?(dot_product(v), length * v.length) end |