How can I check if 2 ranges intersect in any way in ruby?

You can check if one range includes begin or end of another one:

r1 = (1..5)
r2 = (4..8)

r1.include?(r2.begin) || r1.include?(r2.end) || r2.include?(r1.begin) || r2.include?(r1.end)

In your case:

r1 = (sprite1[1]..sprite1[1] + sprite1[0].width)
r2 = (sprite2[1]..sprite2[1] + sprite2[0].width)
r1.include?(sprite2[1]) || r1.include?(sprite2[1] + sprite2[0].width) || 
  r2.include(sprite1[1]) || r2.include(sprite1[1] + sprite1[0].width)

assuming that the ranges are not endless.


There are a number of cases to consider, as pointed out by @muistooshort in a comment.

I assume that the elements of the ranges are integers, as in the example.

Code

The easiest way to determine if two ranges intersect is to negate an expression that determines if they don't intersect.

def overlap?(r1, r2)
  return false if r1.size.zero? || r2.size.zero?
  !(range_last(r1) < r2.begin || range_last(r2) < r1.begin) 
end

def range_last(r)
  return Float::INFINITY if r.end==Float::INFINITY || r.end.nil?
  r.include?(r.end) ? r.end : r.end-1  
end

Examples

overlap? 1..3, 2..5                               #=> true 
overlap? 1..3, 4..5                               #=> false 
overlap? 3..1, 0..5                               #=> false 
overlap? 1..4, 4..8                               #=> true 
overlap? 1...4, 4..8                              #=> false 
overlap? 4.., 1..4                                #=> true 
overlap? 4.., 1...4                               #=> false 
overlap? -Float::INFINITY..Float::INFINITY, 1..1  #=> true
overlap? -Float::INFINITY..7, 7..8                #=> true
overlap? -Float::INFINITY...7, 7..8               #=> false 
overlap? -4..Float::INFINITY, -6..-4              #=> true 
overlap? -4..Float::INFINITY, 6...-4              #=> false 

Observe that the range 3..1 in the 3rd example is empty.

Note also:

range_last(1..)                                   #=> Infinity 
range_last(1..Float::INFINITY)                    #=> Infinity 
range_last(1..3)                                  #=> 3 
range_last(1...3)                                 #=> 2 

If the two ranges, r1 and r2, are finite and of the two-dot variety one can replace range_last(r) with r.last.

Tags:

Ruby

Range