Аналогичный вопрос был задан здесь, но с упором на эффективность.
Подход scipy.spatial.ConvexHull
, предложенный здесь @Brian и @fatalaccidents работает, но работает очень медленно, если вам нужно проверить более одной точки.
Что ж, наиболее эффективное решение также исходит из scipy.spatial
, но использует тесселяцию Delaunay
:
from scipy.spatial import Delaunay
Delaunay(poly).find_simplex(point) >= 0 # True if point lies within poly
Это работает, потому что -1
возвращает .find_simplex(point)
, если точка не находится ни в одном из симплексов (т.е. вне триангуляции). (Примечание: он работает в N измерениях, а не только в 2/3D.)
Сравнение производительности
Сначала за один балл:
import numpy
from scipy.spatial import ConvexHull, Delaunay
def in_poly_hull_single(poly, point):
hull = ConvexHull(poly)
new_hull = ConvexHull(np.concatenate((poly, [point])))
return np.array_equal(new_hull.vertices, hull.vertices)
poly = np.random.rand(65, 3)
point = np.random.rand(3)
%timeit in_poly_hull_single(poly, point)
%timeit Delaunay(poly).find_simplex(point) >= 0
Результат:
2.63 ms ± 280 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.49 ms ± 153 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Таким образом, подход Delaunay
быстрее. Но это зависит от размера полигона! Я обнаружил, что для многоугольника, состоящего более чем из ~65 точек, подход Delaunay
становится все более медленным, в то время как подход ConvexHull
остается почти постоянным по скорости.
Для нескольких точек:
def in_poly_hull_multi(poly, points):
hull = ConvexHull(poly)
res = []
for p in points:
new_hull = ConvexHull(np.concatenate((poly, [p])))
res.append(np.array_equal(new_hull.vertices, hull.vertices))
return res
points = np.random.rand(10000, 3)
%timeit in_poly_hull_multi(poly, points)
%timeit Delaunay(poly).find_simplex(points) >= 0
Результат:
155 ms ± 9.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1.81 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Таким образом, Delaunay
дает экстремальное увеличение скорости; не говоря уже о том, как долго нам придется ждать 10 000 баллов или более. В таком случае размер полигона больше не имеет большого значения.
Таким образом, Delaunay
не только намного быстрее, но и очень лаконичен в коде.
person
BottleNick
schedule
13.03.2020