这是一个分而治之的算法;这个想法与quicksort 大体相似。我假设矩形不重叠,并且它们都包含在边界框中,尽管边界可能会接触。
- 如果边界框退化(即零宽度或零高度),则什么也不做。
- 否则,如果没有矩形,则只生成边界框本身。
- 否则:
- 从列表中选择一个矩形作为“轴”。
- 为枢轴“上方”、“左”、“右”和“下方”创建新的边界框。
- 通过过滤与每个新边界框相交的矩形列表,并将它们剪切到边界框,构建枢轴“上方”、“左侧”、“右侧”和“下方”的矩形列表。
- 用新的矩形列表分别递归地细分“上方”、“左侧”、“右侧”和“下方”边界框。
每个矩形最多可以参与 4 次递归调用中的 2 次。如果枢轴是随机选择的,并且大多数矩形不与大多数其他矩形垂直重叠,那么平均每个矩形都参与一个递归调用。由于非递归工作需要线性时间,因此这种情况下的预期运行时间为 O(n log n),其中 n 是矩形的数量。
Python 中的实现:
import random
from collections import namedtuple
Rectangle = namedtuple('Rectangle', 'x1 y1 x2 y2')
def intersects(b, r):
return b.x1 < r.x2 and b.x2 > r.x1 and b.y1 < r.y2 and b.y2 > r.y1
def clip_rect(b, r):
return Rectangle(
max(b.x1, r.x1), max(b.y1, r.y1),
min(b.x2, r.x2), min(b.y2, r.y2)
)
def clip_rects(b, rects):
return [clip_rect(b, r) for r in rects if intersects(b, r)]
def split_rectangles(b, rects):
if b.x1 >= b.x2 or b.y1 >= b.y2:
pass
elif not rects:
yield b
else:
# randomize to avoid O(n^2) runtime in typical cases
# change this if deterministic behaviour is required
pivot = random.choice(rects)
above = Rectangle(b.x1, b.y1, b.x2, pivot.y1)
left = Rectangle(b.x1, pivot.y1, pivot.x1, pivot.y2)
right = Rectangle(pivot.x2, pivot.y1, b.x2, pivot.y2)
below = Rectangle(b.x1, pivot.y2, b.x2, b.y2)
yield from split_rectangles(above, clip_rects(above, rects))
yield from split_rectangles(left, clip_rects(left, rects))
yield from split_rectangles(right, clip_rects(right, rects))
yield from split_rectangles(below, clip_rects(below, rects))
示例:如您所见,它没有使用最少数量的矩形,因为右侧有两个可以垂直连接在一起。
如果最小化矩形的数量很重要,您可能需要考虑“上方”、“左侧”、“右侧”和“下方”的不同边界框,并再次检查结果以将所有矩形连接在一起如果它们有两条边相等的线段。