Understanding Overlap: in 1 or 2 dimensions

October 9, 2022

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:

How can 2 rectangles overlap in one dimension?

examples of overlap with rectangles

There seems to be a lot of cases 🤔

show partial overlap

But what about this?

show complete overlap

Both sides of B are inside A. But neither of A’s sides are inside B. We need symmetry…

What are we left with?

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?

revisit two rectangles that do not overlap

in English:

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:

// 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?

identical rectangle in perfect overlap

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.

Discuss on Twitter