Skip to content

算法Algorithm

扫描线划分单调多边形

Monotone polygon partition · MakeMonotone

向下扫描顶点,用活动左边界的 helper 消除 split/merge 顶点,构造互不相交的单调分块。

形式陈述 ​

怎样把任意无孔简单多边形切成容易线性剖分的几块?称一个多边形为 y-单调,如果每条水平线与它的交为空、点或一条线段。等价地,从最高点到最低点的两条边界链都不向上折返。

输入顶点 v0,…,vn−1 逆时针排列,边 ei=vivi+1,下标按 n 循环。先假定不同顶点高度不同,向下执行扫描线。看一个顶点的两个邻居在它上方还是下方,再看内角是否小于 π,得到五类事件:

邻居位置 凸角 凹角
两邻居都在下方 start split
两邻居都在上方 end merge
一上一下 regular regular

split 让一个横截区间分裂,merge 让两个横截区间合并。没有 split 或 merge 的简单多边形就是单调多边形:若某水平线切出两个区间,沿两区间之间的凹口边界向上或向下走,必然遇到一个凹的局部最高点或最低点,恰是这两类之一。

状态只保存横截区间的左边界。每条活动边 e 附一个 helper(e):它是已处理区域中,与 e 所界定内部条带相关的最低已处理可连接顶点。更具体地,其所在高度的水平线段可以从它向左连到 e,线段内部仍在多边形内。helper 被更新时,旧 helper 若为 merge,代表一项尚未完成的向下连接义务。

直觉

split 已经看见了上方,马上可以向上接一条合法对角线;merge 需要向下接线,但扫描还没到未来顶点,只能把这项义务存进 helper。以后有人替换这个 helper,或对应边走到终点,便在覆盖它之前先补上对角线。

每次选择左邻边并非为了“就近猜一个点”。helper 与新顶点之间的扫描条带没有被遗漏的顶点事件;若有,helper 早已被更新。由边界不相交和空条带可知,新对角线留在该条带内,并不穿越原边或先前对角线。这是可见性证据,也是局部操作能够组合成全局剖分的原因。

helper 的待连接义务
例子与边界

五类事件的实际操作 ​

令 finish(e,v) 表示:若 helper(e) 为 merge,就连接它与 v。连接始终指插入内部对角线,并同步更新DCEL 或等价的面边界记录。

  • start:插入 ei,令其 helper 为 vi
  • end:执行 finish(e_{i-1},v_i),然后删除 ei−1
  • split:找到 vi 正左方的活动边 e,连接 vi 与 helper(e),把 helper(e) 改为 vi;再插入 ei,其 helper 也设为 vi
  • merge:先完成并删除 ei−1,再找正左方边 e,执行 finish(e,v_i),将 helper(e) 改为 vi
  • regular:若后继 vi+1 在下方,内部在扫线穿越该顶点时位于右侧,完成并删除 ei−1,插入 ei 并令 helper(ei)=vi;否则只找左邻边,完成它的旧 helper 义务并更新 helper

这些操作说明为什么不能只在遇到 merge 当时接线:它需要的下方顶点尚未出现。也不能只在 split 事件检查 helper,否则由 regular 事件接手的 merge 会永远被漏掉。

十边形的一次完整扫描 ​

沿耳切页的十边形编号,采用高度 h(x,y)=y+x/1000,消除水平边引起的同高事件。这是可逆剪切变换,所有叉积符号保持不变;对这个有界整数实例,选择的系数确实让十个高度互不相同。

事件次序为

2,3,8,9,6,7,4,5,1,0,

类型分别是

start, regular, start, regular, start, merge,regular, merge, regular, end.

处理 v7 后,活动边 e9=v9v0 的 helper 变为 v7,但暂不连接。到 v5 时,这项 merge 义务产生对角线 v5v7,然后 helper 变为 v5。到 v1 时,regular 分支再补上 v1v5。最终得到三个面

(5,6,7),(1,2,3,4,5),(5,7,8,9,0,1).

沿每一面的两条边界链检查 h,从最高点到最低点都严格下降。这比只检查“所有凹角似乎都接了线”更直接:输出的目标就是单调性,而不是对角线数量越多越好。

水平边可以用符号剪切 h=y+εx 定义事件次序,比较按 (y,x) 的字典序进行;几何方向判定仍使用原坐标。若采用实际有理剪切,应像本例一样证明或直接检查高度互异,不能对任意实数坐标无条件写死 10−3。边界共线还需要一致的平角约定和不产生零面积分块的处理。

推论与应用

活动边使用支持最坏对数时间更新的平衡搜索树,例如 AVL 树或红黑树。每次事件只做常数次插入、删除、左邻查询;顶点先经比较排序排列,排序及总维护时间为 O(nlog⁡n),空间 O(n)。边之间不相交,所以两个事件之间的横向顺序不变;这才允许比较器依赖当前高度。任意改变扫线位置、却不按事件维护顺序,会破坏树的有序性。

本单元的核验脚本故意用直接扫描活动边寻找左邻,使 helper 状态容易逐行审计;这个参考实现是 O(n2),不是平衡树实现的速度证据。它核验插入对角线可见、分块两链单调、面积和保持,再由线性栈剖分验证每块的三角形数。

加入 d 条对角线后,各块顶点数之和为 n+2d=O(n),因为每条对角线只在两面各计一次。因此后续逐块线性剖分的总成本仍是 O(n),不会把分块后的重复边界误计成 O(n2)。

参考资料
  • Mark de Berg 等,Computational Geometry: Algorithms and Applications,第 3 版,2008,§3.2,Lemma 3.4、MakeMonotone 及五类事件处理,印刷 pp.49–55。课程原书
  • Dave Mount,Polygon Triangulation,2026,单调分块与两阶段剖分。讲义使用水平单调方向;本页选择竖直扫描并明确剪切约定。
关系图谱12 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系