我知道用于查找点是否在任何多边形内的标准光线投射算法。但是,如果将自己局限于凸多边形,是否有更快的方法?
答案 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;
}