如何判断一个点是否在多边形内?

凸多边形

问题

给定一个点o和平面中三个点a、b、c,判断o是否在三角形内部

解决

可以通过面积算,即判断o点与另外两个点构成的三角形面积之和是否等于三角形abc的面积。
在这里插入图片描述
面积可以通过海伦公式计算,即假设三边长分别为a、b、c, s = p ( p − a ) ( p − b ) ( p − c ) , p = a + b + c 2 s=\sqrt{p(p-a)(p-b)(p-c)}, p=\frac{a+b+c}{2} s=p(pa)(pb)(pc) ,p=2a+b+c

但是这种方法可能因为浮点数精度问题导致误差,所以最好使用叉乘法。
在这里插入图片描述
有图可知,按逆时针顺序得到的向量,点o在他们的左侧即表示在三角形内部。AB*AO>0则表示o在AB左侧,叉乘公式为 ( x 1 ∗ y 2 − x 2 ∗ y 1 ) (x_1*y_2-x_2*y_1) (x1y2x2y1)

代码:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y


def product(p1, p2, p3):
    # 叉乘
    return (p2.x - p1.x) * (p3.y - p1.y) - \
           (p2.y - p1.y) * (p3.x - p1.x)


def solution(p1, p2, p3, o):
    # 确保逆时针,即p3在p1p2的左侧,否则调换位置
    if product(p1, p2, p3) < 0:
        return solution(p1, p3, p2, o)
    # o在三角形内部,则在逆时针每个向量的左侧,均>0
    if product(p1, p2, o) > 0 and \
            product(p2, p3, o) > 0 and \
            product(p3, p1, o) > 0:
        return True
    return False

也有可能要判断三个点能否构成三角形,只要任意两边斜率不相等即可。

https://leetcode-cn.com/circle/discuss/7OldE4/

非凸多边形

非凸多边形就不能用叉乘的方式,但如果一个点在多边形内部,以其为点向任意方向做射线,则必然存在奇数个交点,在多边形外则一定偶数个交点(0也是偶数)。

import collections
# polygon: list of points
def IsPointInPolygon(point, polygon):
    lines = collections.defaultdict()
    for i in range(len(polygon) - 1):
        for j in range(i+1, len(polygon)):
            c = 0
            if polygon[i].x - polygon[j].x == 0:
                a = polygon[i].x
                c = 1
            else:
                a = (polygon[i].y - polygon[j].y) / (polygon[i].x - polygon[j].x)
            b = polygon[i].y - a * polygon[i].x
            lines[i, j] = [a, b, c]
    a = 1
    b = point.y - point.x
    cnt = 0
    for i, j in lines.keys():
        c, d, e = lines[i, j][0], lines[i, j][1], lines[i, j][2]
        if e:
            x = c
        else:
            x = (d - b) / (a - c)
        y = a * x + b
        if (y - polygon[i].y) * (y - polygon[j].y) < 0 and (y - point.y) / (x - point.x) > 0:
            cnt += 1
    if cnt % 2:
        return True
    return False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值