在计算机图形学中,判断点是否在多边形内部是一个常见的问题。这里介绍两种常用的方法—射线法和回转数,并提供相应的理论推导和代码实现。

射线法

理论推导

射线法的基本思想是从待判断的点出发,向任意方向发射一条射线,然后计算这条射线与多边形边界的交点数目。如果交点数为奇数,则该点在多边形内部;如果为偶数,则在外部。

具体步骤如下:

  1. 选择一个方向,从点P出发画一条射线。
  2. 遍历多边形的每条边,检查射线是否与该边相交。
  3. 如果射线与边相交,且交点在射线的延长线上(即交点的x坐标大于点P的x坐标),则交点数加1。
  4. 遍历完所有边后,如果交点数为奇数,则点P在多边形内部;如果为偶数,则在外部。

C++代码实现

#include <iostream>
#include <vector>

struct Point {
    double x, y;
};

// 计算叉积,用于判断点P是否在A和B的连线上
double cross(const Point& A, const Point& B, const Point& C) {
    return (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x);
}

// 判断点是否在多边形内部
bool isPointInPolygon(const std::vector<Point>& polygon, const Point& P) {
    int wn = 0; // 交点数
    int n = polygon.size();
    for (int i = 0; i < n; i++) {
        const Point& A = polygon[i];
        const Point& B = polygon[(i + 1) % n];
        if (A.y <= P.y && B.y > P.y && cross(B, P, A) > 0) {
            wn++;
        } else if (A.y > P.y && B.y <= P.y && cross(B, P, A) < 0) {
            wn--;
        }
    }
    return wn != 0; // 如果交点数不为0,则点在多边形内部
}

int main() {
    // 多边形顶点坐标
    std::vector<Point> polygon = {{0, 0}, {2, 0}, {1, 2}};
    // 待判断点的坐标
    Point P = {1, 1};
    // 判断点与多边形的位置关系
    if (isPointInPolygon(polygon, P)) {
        std::cout << "Point P is inside the polygon." << std::endl;
    } else {
        std::cout << "Point P is outside the polygon." << std::endl;
    }
    return 0;
}

Python代码实现

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

def cross(A, B, C):
    # 计算叉积
    return (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)

def is_point_in_polygon(polygon, P):
    wn = 0  # 交点数
    n = len(polygon)
    for i in range(n):
        A = polygon[i]
        B = polygon[(i + 1) % n]
        if A.y <= P.y and B.y > P.y and cross(B, P, A) > 0:
            wn += 1
        elif A.y > P.y and B.y <= P.y and cross(B, P, A) < 0:
            wn -= 1
    return wn != 0  # 如果交点数不为0,则点在多边形内部

if __name__ == "__main__":
    # 多边形顶点坐标
    polygon = [Point(0, 0), Point(2, 0), Point(1, 2)]
    # 待判断点的坐标
    P = Point(1, 1)
    # 判断点与多边形的位置关系
    if is_point_in_polygon(polygon, P):
        print("Point P is inside the polygon.")
    else:
        print("Point P is outside the polygon.")

回转数

回转数算法(Winding Number Algorithm)是一种判断点是否在多边形内部的算法。回转数是指从点P出发,沿着多边形的边界走一圈,回到起点时,点P绕了多少圈。如果回转数为0,则点P在多边形外部;如果回转数不为0,则点P在多边形内部。

理论推导

  1. 向量叉乘:对于多边形的每条边,计算从点P到边的两个端点的向量的叉乘。叉乘的符号可以告诉我们向量的旋转方向。

  2. 角度计算:通过叉乘的符号,我们可以计算出每条边对点P的回转贡献。如果叉乘为正,表示逆时针旋转,贡献为1;如果叉乘为负,表示顺时针旋转,贡献为-1。

  3. 回转数累加:遍历多边形的所有边,累加每条边的回转贡献。如果点P在多边形内部,累加的结果将是一个非零整数;如果点P在多边形外部,累加的结果将为0。

C++代码实现

#include <iostream>
#include <vector>
#include <cmath>

struct Point {
    double x, y;
};

double cross(const Point& O, const Point& A, const Point& B) {
    return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
}

int windingNumber(const std::vector<Point>& polygon, const Point& P) {
    int wn = 0;
    for (size_t i = 0; i < polygon.size(); ++i) {
        Point A = polygon[i], B = polygon[(i + 1) % polygon.size()];
        if (A.y <= P.y && B.y > P.y) {
            if (cross(P, A, B) > 0) {
                ++wn;
            }
        } else if (A.y > P.y && B.y <= P.y) {
            if (cross(P, A, B) < 0) {
                --wn;
            }
        }
    }
    return wn;
}

bool isPointInPolygon(const std::vector<Point>& polygon, const Point& P) {
    return windingNumber(polygon, P) != 0;
}

int main() {
    std::vector<Point> polygon = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
    Point P = {0.5, 0.5};
    if (isPointInPolygon(polygon, P)) {
        std::cout << "Point is inside the polygon." << std::endl;
    } else {
        std::cout << "Point is outside the polygon." << std::endl;
    }
    return 0;
}

Python代码实现

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

def cross(O, A, B):
    return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x)

def winding_number(polygon, P):
    wn = 0
    for i in range(len(polygon)):
        A = polygon[i]
        B = polygon[(i + 1) % len(polygon)]
        if A.y <= P.y and B.y > P.y:
            if cross(P, A, B) > 0:
                wn += 1
        elif A.y > P.y and B.y <= P.y:
            if cross(P, A, B) < 0:
                wn -= 1
    return wn

def is_point_in_polygon(polygon, P):
    return winding_number(polygon, P) != 0

# Example usage
polygon = [Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)]
P = Point(0.5, 0.5)
if is_point_in_polygon(polygon, P):
    print("Point is inside the polygon.")
else:
    print("Point is outside the polygon.")
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐