geometry
rectangle combination
shape merging
spatial optimization
mathematical concepts

Combine smaller rectangles into larger ones

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Combining small rectangles into larger ones sounds easy until you define exactly what combine means. If the rectangles merely touch, overlap, or form an irregular outline, the union may not itself be a rectangle. A correct algorithm has to preserve geometry rather than just group nearby shapes.

For axis-aligned rectangles, the usual goal is to merge two rectangles only when their union is still one valid rectangle. That gives you a reliable simplification step for tiling, image regions, collision boxes, and layout compression.

Define the Merge Rule First

Represent each rectangle as (x1, y1, x2, y2), where x1 < x2 and y1 < y2. Two rectangles can be merged into one larger rectangle only if:

  • They share a full edge horizontally or vertically.
  • Their union has no gaps or L-shape.
  • The result is still axis-aligned.

For example, these can merge:

  • '(0, 0, 2, 1) and (2, 0, 5, 1)'
  • '(1, 2, 3, 4) and (1, 4, 3, 6)'

But these cannot:

  • '(0, 0, 2, 1) and (2, 0, 5, 2) because heights differ.'
  • '(0, 0, 2, 2) and (2, 1, 4, 3) because the union is not a single rectangle.'

A Practical Merge Function

The simplest safe merge rule checks for exact alignment on one axis and shared borders on the other:

python
1def try_merge(a, b):
2    ax1, ay1, ax2, ay2 = a
3    bx1, by1, bx2, by2 = b
4
5    # Horizontal merge
6    if ay1 == by1 and ay2 == by2:
7        if ax2 == bx1:
8            return (ax1, ay1, bx2, ay2)
9        if bx2 == ax1:
10            return (bx1, by1, ax2, by2)
11
12    # Vertical merge
13    if ax1 == bx1 and ax2 == bx2:
14        if ay2 == by1:
15            return (ax1, ay1, ax2, by2)
16        if by2 == ay1:
17            return (bx1, by1, bx2, ay2)
18
19    return None
20
21print(try_merge((0, 0, 2, 1), (2, 0, 5, 1)))
22print(try_merge((0, 0, 2, 1), (2, 0, 5, 2)))

This deliberately avoids over-merging. If the union is not obviously one rectangle, it returns None.

Repeatedly Merge a Whole Set

To simplify a full list of rectangles, run the merge step until no more valid merges are found:

python
1def combine_rectangles(rectangles):
2    rectangles = list(rectangles)
3    changed = True
4
5    while changed:
6        changed = False
7        result = []
8        used = [False] * len(rectangles)
9
10        for i in range(len(rectangles)):
11            if used[i]:
12                continue
13
14            merged = rectangles[i]
15            for j in range(i + 1, len(rectangles)):
16                if used[j]:
17                    continue
18
19                candidate = try_merge(merged, rectangles[j])
20                if candidate is not None:
21                    merged = candidate
22                    used[j] = True
23                    changed = True
24
25            used[i] = True
26            result.append(merged)
27
28        rectangles = result
29
30    return rectangles
31
32rects = [
33    (0, 0, 2, 1),
34    (2, 0, 4, 1),
35    (4, 0, 6, 1),
36    (0, 1, 2, 2),
37]
38
39print(combine_rectangles(rects))

This greedy approach is easy to understand and works well when the rectangles come from a clean tiling. For more complex spatial data, you may want sweep-line logic or region union algorithms.

When the Problem Is Harder Than It Looks

Some datasets do not contain perfect rectangle tilings. Overlaps, tiny gaps, floating-point coordinates, and fragmented grids all complicate merging. In those cases, it is often better to rasterize onto a grid or use a geometry library that computes polygon unions first, then re-extract rectangular regions if possible.

Another common variation is maximizing area coverage with the fewest rectangles. That is no longer just a local merge problem. It becomes an optimization problem, and greedy pairwise merging may not produce the best final set.

Why Order Matters

Greedy merging can be order-sensitive. Suppose three rectangles can merge in pairs, but one merge creates a larger rectangle that blocks a different, more useful merge later. If optimality matters, you need either a better search strategy or a domain-specific merge order.

For many engineering tasks, though, local correctness matters more than global optimality. If every merge is guaranteed to preserve a valid rectangle and the data comes from a grid, iterative merging is usually enough.

Common Pitfalls

The most common mistake is merging rectangles that merely touch at a corner. Corner contact does not form a larger rectangle.

Another pitfall is ignoring exact edge alignment. Two rectangles that overlap partially may have a rectangular union, but they may also create irregular geometry if merged naively.

Floating-point coordinates are also tricky. If rectangles come from rendering or image analysis, values that should match may differ slightly. In that case, use a tolerance carefully rather than exact equality, but be aware that tolerances can also create false merges.

Finally, do not confuse merge adjacent rectangles with compute union of arbitrary rectangles. The second problem is broader and often needs full polygon-union logic.

Summary

  • Merge rectangles only when their union is still exactly one rectangle.
  • A safe merge requires full edge alignment horizontally or vertically.
  • A simple iterative algorithm works well for clean axis-aligned tilings.
  • Greedy merging is easy to implement but may be order-sensitive.
  • Arbitrary overlaps and irregular unions usually require more general geometry processing.

Course illustration
Course illustration

All Rights Reserved.