如何确定一个点是否在一个2D凸多边形内部比N时间更快

时间:2014-01-13 15:32:12

标签: algorithm geometry

我知道用于查找点是否在任何多边形内的标准光线投射算法。但是,如果将自己局限于凸多边形,是否有更快的方法?

2 个答案:

答案 0 :(得分:7)

是的,您可以使用二进制搜索。你可以通过递归切割多边形到它的大小的一小部分(即一半)并检查你是哪一边来做到这一点。例如,您可以从检查通过顶点0和顶点n / 2的线上的正侧或负侧开始。一旦你有3个顶点,你只需测试剩下的两个边,完成测试与那个三角形。

这里有一些伪代码,希望这更容易理解:

function TestConvexPolygon(point, polygon)
  if polygon.size == 3 then
    return TestTriangle(point, polygon) // constant time

  if (TestLine(point, polygon[0], polygon[polygon.size/2]) > 0)
    return TestConvexPolygon(point, new polygon from polygon.size/2 to polygon.size-1 and 0)
  else
    return TestConvexPolygon(point, new polygon from 0 to polygon.size/2)

另一种可视化想法的方法是,您可以将多边形视为三角形扇形。然后,您首先测试您的点与中间内边缘的对比。这将消除风扇中一半可能的三角形。由于半个三角扇仍然是一个三角扇,你可以递归地执行此操作,直到你的扇子中只剩下一个三角形,然后你可以明确地测试它。

一个真正的实现需要一些索引杂耍,但其他方面也很简单。

答案 1 :(得分:0)

正如答案所述,该算法是递归的。在每一步中,您都会切掉多边形不能指向的部分。这是一个C ++代码:

#include "stdafx.h"
#include <vector>
#include <iostream>

struct vec2d {
    double x, y;
    vec2d(double _x, double _y) : x(_x), y(_y) {}
};

// Finds the cross product of the vectors: AB x BC
double crossProduct(vec2d pointA, vec2d pointB, vec2d pointC) {
    vec2d vectorAB = vec2d(pointB.x - pointA.x, pointB.y - pointA.y);
    vec2d vectorBC = vec2d(pointC.x - pointB.x, pointC.y - pointB.y);

    return vectorAB.x * vectorBC.y - vectorBC.x * vectorAB.y;
}

// Finds area for the triangle ABC
double S(vec2d A, vec2d B, vec2d C) {
    return crossProduct(A, B, C) / 2;
}

bool isPointInsideTriangle(vec2d A, vec2d B, vec2d C, vec2d point)
{
    return S(A, B, point) >= 0 && S(B, C, point) >= 0 && S(C, A, point) >= 0;
}

bool isPointAboveLine(vec2d A, vec2d B, vec2d point)
{
    return S(A, B, point) >= 0;
}

// O(logN), works only for convex polygons
bool isPointInsidePolygon(std::vector<vec2d> polygon, vec2d point) {
    if (polygon.size() == 3) {
        return isPointInsideTriangle(polygon[0], polygon[1], polygon[2], point);
    }

    if (isPointAboveLine(polygon[0], polygon[polygon.size() / 2], point)) {
        std::vector<vec2d> polygonAbove(polygon.begin() + polygon.size() / 2, polygon.end());
        polygonAbove.emplace(polygonAbove.begin(), polygon[0]);
        return isPointInsidePolygon(polygonAbove, point);
    }
    else {
        std::vector<vec2d> polygonBelow(polygon.begin(), polygon.begin() + polygon.size() / 2 + 1);
        return isPointInsidePolygon(polygonBelow, point);
    }
}

int main()
{
    std::vector<vec2d> convexPolygon;
    convexPolygon.push_back(vec2d(0, 2));
    convexPolygon.push_back(vec2d(2, 0));
    convexPolygon.push_back(vec2d(4, 1));
    convexPolygon.push_back(vec2d(6, 3));
    convexPolygon.push_back(vec2d(6, 4));
    convexPolygon.push_back(vec2d(5, 6));
    convexPolygon.push_back(vec2d(2, 6));
    convexPolygon.push_back(vec2d(1, 4));
    std::cout << isPointInsidePolygon(convexPolygon, vec2d(2, 5));

    return 0;
}