Understanding Overlap: in 1 or 2 dimensions
tl;dr
The answer is on stackoverflow:
if (RectA.Left < RectB.Right && RectA.Right > RectB.Left &&
RectA.Top > RectB.Bottom && RectA.Bottom < RectB.Top)
which is correct but not especially obvious.
A LOT of people think the stackoverflow answer is wrong and try to “fix” it 🤣
Step by step: in one dimension
Let’s simplify the problem:
- let’s focus only on one dimension – the x axis
- let’s use rectangles – just ignore their heights
(it’s easier to visualize than line segments)
How can 2 rectangles overlap in one dimension?
There seems to be a lot of cases 🤔
- left of B could be inside A
- (or) right of B could be inside A
But what about this?
Both sides of B are inside A. But neither of A’s sides are inside B. We need symmetry…
What are we left with?
- left of B could be inside A
- (or) right of B could be inside A
- (or) left of A could be inside B
- (or) right of A could be inside B
Simple? Did we cover all the cases? (no! read until the end)
Personally, that’s when I turned to stackoverflow.
A different perspective
If overlap is hard to pin down, let’s try to define the opposite: what does it look like when rectangles do NOT overlap?
in English:
- A is to the left of B
- (or) A is to the right of B
let’s turn this to pseudo-code:
no_overlap = a.right < b.left || a.left > b.right
// in other words, overlap is the inverse
overlap = !(a.right < b.left || a.left > b.right)
De Morgan’s Laws say that:
- not (A or B) = (not A) and (not B)
- not (A and B) = (not A) or (not B)
// unchanged, from above
overlap = !(a.right < b.left || a.left > b.right)
// applying De Morgan: conditions inverted, || becomes &&
overlap = !(a.right < b.left) && !(a.left > b.right)
// flipping > and < comparisons
overlap = a.right > b.left && a.left < b.right
// reorganizing order of conditions (to match stackoverflow...)
overlap = a.left < b.right && a.right > b.left
Looping back to the beginning, check the first line of answer:
if (RectA.Left < RectB.Right && RectA.Right > RectB.Left &&
RectA.Top > RectB.Bottom && RectA.Bottom < RectB.Top)
What about 2d?
The same “to-the-left-of” or “to-the-right-of” cases apply. You also need to consider “above” and “below” cases.
(top and bottom are relative to … the direction of your y-axis) 😬
// unchanged, from above
overlap = !(a.right < b.left || a.left > b.right ||
a.bottom > b.top || a.top < b.bottom)
// applying De Morgan: conditions inverted, || becomes &&
overlap = !(a.right < b.left) && !(a.left > b.right) &&
!(a.bottom > b.top) && !(a.top < b.bottom)
// flipping > and < comparison
overlap = a.right > b.left && a.left < b.right &&
a.bottom < b.top && a.top > b.bottom
// reorganizing order of conditions (to match stackoverflow...)
overlap = a.left < b.right && a.right > b.left &&
a.top > b.bottom && a.bottom < b.top
Back to this:
if (RectA.Left < RectB.Right && RectA.Right > RectB.Left &&
RectA.Top > RectB.Bottom && RectA.Bottom < RectB.Top)
Edge Case
What case did we miss earlier?
- 2 rectangles
- same dimensions
- exactly on top of each other
In that case, neither rectangles is inside the other… but the stackoverflow solution works for all cases 😄
Discussion
Depending on your situation, the width of your rectangle boundary (1 pixel, or whatever units)
might make a difference: consider changing <
/>
to <=
/>=
if needed.
If you’re still not convinced, try this interactive tool.