该算法是很多繁琐的案例分析。不是超级复杂,但很难完全正确。
假设所有矩形都按左下角和右上角 (x0, y0, x1, y1) 存储在数组 A 中。因此,我们可以将矩形的任何边表示为一对 (e, i),其中 e \in {L, R, T, B} 表示左、右、上、下边,i 表示 A[i]。将所有对 (L, i) 放入起始列表 S 中,并在 A[i].x0 上对其进行排序。
我们还需要一条扫描线 C,它是一个三元组 (T, i, d) 的顶部边缘和底部的 (B, i, d) 的 BST。这里 i 是矩形索引,d 是整数深度,如下所述。BST 的关键是边缘的 y 坐标。最初它是空的。
请注意,您可以随时按顺序遍历 C 并确定扫描线的哪些部分被矩形隐藏而不是隐藏。通过保持深度计数器来做到这一点,最初为零。从最小 y 到最大,当遇到底边时,计数器加 1。当您看到顶部边缘时,减 1。对于计数器为零的区域,扫描线可见。否则它被一个矩形隐藏。
现在你从来没有真正完成整个遍历。相反,您可以通过逐步保持深度来提高效率。C 中每个三元组的 d 元素是其上方区域的深度。(C 中第一条边下方的区域的深度始终为 0。)
最后,我们需要一个输出寄存器 P。它存储一组折线(边的双向链表很方便),并允许查询形式为“给我所有的折线,其末端的 y 坐标落在范围 [y0.. y1]。算法的一个属性是,这些折线总是有两条水平边与扫描线交叉作为它们的末端,而所有其他边都在扫描线的左侧。此外,没有两条折线相交。它们是输出的片段多边形“正在建设中。”请注意,输出多边形可能不是简单的,由多个“循环”和“孔”组成。另一个 BST 将为 P 做。它最初也是空的。
现在算法看起来大致是这样的。我不会偷走弄清楚细节的所有乐趣。
while there are still edges in S
Let V = leftmost vertical edge taken from S
Determine Vv, the intersection of V with the visible parts of C
if V is of the form (L, i) // a left edge
Update P with Vv (polylines may be added or joined)
add (R, i) to S
add (T, i) and (B, i) to C, incrementing depths as needed
else // V is of the form (R, i) // a right edge
Update P with Vv (polylines may be removed or joined)
remove (T, i) and (B, i) from C, decrementing depths as needed
随着 P 的更新,您将生成复杂的多边形。最右边的边缘应该关闭最后一个循环。
最后,请注意重合边缘会产生一些棘手的特殊情况。当您遇到这些时,请再次发布,我们可以讨论。
排序的运行时间当然是 O(n log n),但更新扫描线的成本取决于可以重叠的多边形数量:退化情况为 O(n) 或整个计算为 O(n^2) .
祝你好运。我已经实现了这个算法(几年前)和其他一些类似的算法。它们是严格的逻辑案例分析中的巨大练习。非常令人沮丧,但在您获胜时也会有所收获。