Appearance
扫描线
扫描线把问题拆成按某个坐标排序的事件,并用数据结构维护相邻事件之间的状态。使用扫描线前必须明确:
- 哪些位置会改变状态;
- 同一位置的多个事件如何成批处理;
- 数据结构中的顺序在扫描过程中为何仍然有效;
- 端点接触、重合和退化对象是否计入答案。
轴对齐矩形面积并
给出
将所有
再统一处理该横坐标的所有加入、删除事件。同一横坐标必须分组,否则事件的任意先后容易污染状态语义。区间通常按半开形式
判断线段集中是否存在交点
朴素做法需要
一般位置下的简化算法
先假设所有线段均非竖直、端点横坐标互不相同,并且不存在共端点、端点落在其它线段上、共线重合或三线共点等退化情况。事件只有左、右端点:
- 遇到左端点时插入该线段,并检查它与活动集合中前驱、后继是否相交;
- 遇到右端点时,记录该线段原来的前驱、后继,删除后检查这两个新相邻的线段是否相交。
活动集合按线段在当前横坐标处的
因此,依赖当前横坐标的 set 比较器不是无条件可用:必须保证上面的不变量。比较器还必须形成严格弱序;相同 eps 判等。整数坐标可以通过有理数交叉相乘进行精确比较,并使用足够宽的中间类型。
在上述一般位置假设下,每条线段至多插入、删除一次,每次只检查常数个相邻项,时间复杂度为 set。
同横坐标与退化情况
若要支持任意闭线段,不能只给同一横坐标的事件规定一个简单的全局先后顺序,而应成批处理。所有在该横坐标开始、结束或退化为竖线的线段在该位置都有效;必须先完成共端点、端点在线段上等接触检查,再按扫描线左侧的次序删除结束线段,并按右侧的次序插入开始线段。若只把严格内点相交计入答案,则事件批处理和线段相交谓词都要相应排除端点接触。
竖线段没有单一的
共线重合、零长度线段、一个端点落在另一条线段内部等情况必须使用完整的方向测试和包围盒判断,不能只检查两个严格叉积异号。事件应携带线段唯一编号,删除时应定位这条具体线段;仅按当前
三角形面积并
设输入为
法一:按横坐标分条积分
事件横坐标至少包括所有三角形顶点、所有非重合边交点,以及共线重合段端点的横坐标。将不同事件横坐标排序后,在两个相邻事件
这里必须使用开竖条内活动边给出的单侧极限,并维护竖截区间的并,而不只是给边排序。同一横坐标的顶点和交点应统一处理;竖直边只位于事件位置,本身没有正宽度。共端点、多边共点、相切和共线重合边还需要统一的去重与覆盖计数规则。
一个容易实现但较慢的版本,可以在每个竖条中点求出所有三角形的截线区间,排序合并,并用同一组边函数计算竖条两端的覆盖长度。共有
法二:构造并集轮廓
三角形并是一个多边形区域,但它可能包含多个不连通分量和孔洞,并且可能出现共线重合边。完整的轮廓法至少需要:
- 将所有非退化三角形统一为逆时针方向;
- 求出所有边交点,共线重合时还要加入重合段端点;
- 按交点参数将每条输入边切成原子子线段;
- 判断每条原子子线段两侧是否分别属于并集内部和外部,只保留分隔内外的线段,并将其定向为并集内部位于左侧;
- 为重合边设置唯一归属规则,删除内部边和重复边;
- 将暴露的有向子线段连接成若干闭环,并累加
在标准右手直角坐标系中,若所有边都定向为并集内部位于左侧,则外轮廓为逆时针、孔洞轮廓为顺时针,因此有向面积会自动扣除孔洞。仅检查子线段中点是否位于其它三角形内部,只在无重合的一般位置下才可能足够;一般输入需要检查线段两侧的局部覆盖状态并去重。
显式构造交点或完整轮廓时,复杂度必须包含 eps 合并事件可能改变平面图拓扑。
