如何判断一个多边形是否合法 (Swift 代码实现)

背景及问题分析

利用无人机对一片区域进行测绘前,咱们会先在地图上框选一个区域,而后再规划飞行的路线,而须要测绘的这片区域每每是一个多边形。在 MeshKit iOS 中,咱们加入了多边形区域的编辑功能,其中就涉及判断用户所编辑出来的多边形是否合法的问题。算法

首先咱们要肯定一个标准:怎么样才算一个不合法的多边形 ?咱们能够简单地经过下面这幅图来解释一下: ide

咱们能够看出前面两个分别是凹多边形和凸多边形,而最后一张则是咱们所说的不合法多边形,能够看出这个不合法的多边形的特征就是:它存在某条边与另一条边相交的状况spa

那么要判断一个多边形是否合法,咱们只要判断组成多边形的全部线段是否存在相交的状况便可,固然,咱们这里所说的相交是 规范相交 ,即 交点不在线段的端点上3d

好了,那么如今的问题能够简化成:如何判断两条线段是否规范相交code

算法解析

这里咱们须要借助 向量的叉积 来进行判断。cdn

叉积,又称向量积,是对三维空间中的两个向量的二元运算。视频

这里推荐 3Blue1Brown 的 视频 来快速回顾一下叉积的概念(下面的两幅截图来自此视频)。咱们只需知道叉积的结果是有正负的,好比咱们以向量 \vec{v} 为标准,以下图,向量 \vec{w}\vec{v}顺时针方向,那么 \vec{v} \times \vec{w} < 0blog

若是向量 \vec{w}\vec{v}逆时针方向,那么 \vec{v} \times \vec{w} > 0get

那么咱们如何利用叉积的特性运用到判断线段是否相交上呢?it

咱们先看下面最直接的一个线段相交的状况:

线段 P_1P_2 和 线段 Q_1Q_2 明显存在一个交点,从上面这张图咱们能够作一个简单的结论:若是一条的线段的两个端点在另一条线段两侧,那么这两条线段可能相交,注意这里说的是可能相交,稍后会讲到另一种状况。

咱们能够将上面的图转换为向量的状况来看:

是否是以为似曾相识,这跟上面提到的叉积的状况是否是很相似? 向量 \vec{P_1Q_1}\vec{P_1P_2} 向量 \vec{P_1Q_2}\vec{P_1P_2} 的顺时针方向,那么:\vec{P_1P_2} \times \vec{P_1Q_2} < 0

用 A 表示 \vec{P_1P_2} \times \vec{P_1Q_1} 的叉积结果,用 B 表示 \vec{P_1P_2} \times \vec{P_1Q_2} 的叉积结果,那么 一条的线段的两个端点在另一条线段两侧 这个几何现象能够用这个公式表示 :A{\times}B<0

咱们前面提到 若是一条的线段的两个端点在另一条线段两侧,那么这两条线段可能相交 ,为何是可能相交呢?若是咱们将 线段 Q_1Q_2 往右边移动一下,会存在下面这种状况:

从上图能够看出,线段 Q_1Q_2 的两个端点在线段 P_1P_2 两侧,可是它们并无相交。

那么如何排除这种状况呢?其实很简单,咱们以前都是以线段 P_1P_2 做为主视角,若是将主视角换成线段 Q_1Q_2,那么咱们很容易看出 线段 P_1P_2 的两个端点并无在 线段 Q_1Q_2 的两侧。因此咱们再次看回上面相交的那幅图,为了可以充分的判断两条线段相交,此次以 Q_1Q_2 为主视角看待这个问题,求叉积:

向量 \vec{Q_1P_2}\vec{Q_1Q_2} 的逆时针方向,那么: \vec{Q_1Q_2} \times \vec{Q_1P_2} > 0 向量 \vec{Q_1P_1}\vec{Q_1Q_2} 的顺时针方向,那么: \vec{Q_1Q_2} \times \vec{Q_1P_1} < 0

综上,咱们能够得出:

A = \vec{P_1P_2} \times \vec{P_1Q_1}
B = \vec{P_1P_2} \times \vec{P_1Q_2}
C = \vec{Q_1Q_2} \times \vec{Q_1P_1}
D = \vec{Q_1Q_2} \times \vec{Q_1P_2}

A{\times} B < 0 && C \times D < 0 的时候,两条线段规范相交。 至于向量的叉积如何运算,这里就不细写了,给出一张计算草稿给你们过目一下:

示例代码

根据计算草稿的内容,咱们就很容易经过代码来实现了:

private func isIntersect(line1: (CGPoint, CGPoint), line2: (CGPoint, CGPoint)) -> Bool {
    let p1 = line1.0
    let p2 = line1.1
    let q1 = line2.0
    let q2 = line2.1

    let a1 = (p2.x - p1.x) * (q1.y - p1.y) - (q1.x - p1.x) * (p2.y - p1.y)
    let a2 = (p2.x - p1.x) * (q2.y - p1.y) - (q2.x - p1.x) * (p2.y - p1.y)
    
    let b1 = (q2.x - q1.x) * (p1.y - q1.y) - (p1.x - q1.x) * (q2.y - q1.y)
    let b2 = (q2.x - q1.x) * (p2.y - q1.y) - (p2.x - q1.x) * (q2.y - q1.y)
    
    if a1 * a2 < 0 && b1 * b2 < 0 {
        return true
    }
    return false
}
复制代码

因为笔者能力有限,文中若有错误还请各位读者不吝赐教。

相关文章
相关标签/搜索