如何判断一个多边形是否合法

2019-11-29 13:06:27

参考地址 如何判断一个多边形是否合法  

背景及问题分析

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

首先我们要确定一个标准:怎么样才算一个不合法的多边形 ?我们可以简单地通过下面这幅图来解释一下:


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

那么要判断一个多边形是否合法,我们只要判断组成多边形的所有线段是否存在相交的情况即可,当然,我们这里所说的相交是 规范相交 ,即 交点不在线段的端点上

好了,那么现在的问题可以简化成:如何判断两条线段是否规范相交

算法解析

这里我们需要借助 向量的叉积 来进行判断。

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

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

如果向量 \vec{w}  在  \vec{v}逆时针方向,那么 \vec{v} \times \vec{w} > 0

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

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


线段 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 往右边移动一下,会存在下面这种情况:


从上图可以看出,线段  的两个端点在线段  两侧,但是它们并没有相交。


那么如何排除这种情况呢?其实很简单,我们之前都是以线段 P_1P_2 作为主视角,如果将主视角换成线段 Q_1Q_2,那么我们很容易看出 线段 P_1P_2 的两个端点并没有在 线段 Q_1Q_2 的两侧。所以我们再次看回上面相交的那幅图,为了能够充分的判断两条线段相交,这次以 Q_1Q_2 为主视角看待这个问题,求叉积:


向量   在  的逆时针方向,那么:
向量   在  的顺时针方向,那么:


综上,我们可以得出:
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}




  • 2019-03-15 15:28:33

    10分钟教你搭建自己的ngrok服务器

    内网穿透想必开发过微信的同志都很了解,大部分人选择网上寻找各种现成的,比如ngrok官网、ittun-ngrok、sunny-ngrok或者花生壳之类的。但是世界上没有免费的午餐,要不就是收费,要不就是免费但是偶尔会出现连接失败的问题(当然大多数时间是没有问题的)。

  • 2019-03-15 15:29:53

    丢弃花生壳,搭建自己的ngrok作为内网穿透服务器

    公司没有公网,公司在二级路由下面(就是服务商没有给独立IP,也无法动态获取公网IP),然而公司在开发程序的时候,如对接微信等需要返回数据,或者需要别人能访问我们网址,一般我们就只能购买花生壳内网穿透,但是在使用中发现速度慢,经常掉。所以搭建自己的ngrok服务器来保证内网穿透。

  • 2019-03-15 15:31:21

    内网穿透 ngrok 服务器和客户端配置

    ngrok 简介及作用 ngrok 是一款用 go 语言开发的开源软件,它是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。下图简述了 ngrok 的原理。

  • 2019-03-15 15:32:09

    内网打洞以及代码实现

    假设内网的多个ip或者同一ip的不同port,都要访问同一个(外网ip:port)。对NAT来说,从外网接收的包它的(srcIp:srcPort)==(serverIp,serverPort),它的dstIp==natIp,所以NAT只能用dstPort来决定把这个包转发给哪一个(内网Ip:port)

  • 2019-03-15 15:33:08

    Xshell不能按退格、删除键的解决方案

    在使用xshell时,由于每个服务器不同,一些无法使用Backspace键向后删除字符。针对这个问题,本文为大家解答下退格键无法识别如何设置?

  • 2019-03-15 15:49:28

    win10远程桌面连接不上解决方法

    有朋友就感叹电脑的世界真的是很神奇,可以将整个世界连接在一起。如果别人想要摆弄你的电脑,即使不在一个地方也可以利用远程桌面来控制。而这就是所谓的远程控制操作了,大部分人都知道它的作用,不过这也不排除会遇到一些突发情况的时候,例如win10远程桌面连接不上,这该怎么去解决呢?为此,小编给大家带来了解决的图文操作。

  • 2019-03-15 16:49:18

    Win7无法进入家庭组提示“您的系统管理员不允许访问家庭组”怎么办

     家庭组是家庭网络上可以共享文件和打印机的一组计算机,可以方便用户们共享文件或者视频等,可是最近有win7纯净版系统用户却发现无法进入家庭组,提示“您的系统管理员不允许访问家庭组”,该怎么办呢?现在给大家分享一下Win7无法进入家庭组提示“您的系统管理员不允许访问家庭组”的解决方法。

  • 2019-03-17 22:19:28

    动态更新Toolbar Menu以及Menu中同时显示文字和图标

    我们经常会有这样的需求,在切换Fragment或者点击某个按钮后动态更新Toolbar上Menu项.但是onCreateOptionsMenu方法只在创建Activity的时候调用一次,以后就不再调用了,所以就不能在onCreateOptionsMenu中做处理了。 不过系统提供了另外的一个方法onPrepa