Многоугольник задается как список объектов Vector2I (2-мерные, целые координаты). Как я могу проверить, находится ли данная точка внутри? Все реализации, которые я обнаружил в Интернете, вызывают некий тривиальный контрпример. Кажется, трудно написать правильную реализацию. Язык не имеет значения, поскольку я сам его портирую.
Как проверить, находится ли точка внутри выпуклого многоугольника в двумерных целочисленных координатах?
Ответ 1
Если он выпуклый, то тривиальный способ его проверить состоит в том, что точка лежит на одной стороне всех отрезков (если пересекается в одном порядке).
Вы можете легко это проверить с помощью перекрестного продукта (поскольку он пропорционален косинусу угла, образованного между сегментом и точкой, те с положительным знаком будут располагаться с правой стороны, а с отрицательным знаком на левой стороне).
Вот код в Python:
RIGHT = "RIGHT"
LEFT = "LEFT"
def inside_convex_polygon(point, vertices):
previous_side = None
n_vertices = len(vertices)
for n in xrange(n_vertices):
a, b = vertices[n], vertices[(n+1)%n_vertices]
affine_segment = v_sub(b, a)
affine_point = v_sub(point, a)
current_side = get_side(affine_segment, affine_point)
if current_side is None:
return False #outside or over an edge
elif previous_side is None: #first segment
previous_side = current_side
elif previous_side != current_side:
return False
return True
def get_side(a, b):
x = x_product(a, b)
if x < 0:
return LEFT
elif x > 0:
return RIGHT
else:
return None
def v_sub(a, b):
return (a[0]-b[0], a[1]-b[1])
def x_product(a, b):
return a[0]*b[1]-a[1]*b[0]
Ответ 2
Методы лучей или обмотки являются наиболее распространенными для этой проблемы. Подробнее см. статью Википедии.
Кроме того, проверьте эту страницу для хорошо документированного решения на C.
Ответ 3
Если многоугольник выпуклый, то в С# следующий реализует метод " test if always on same side " и работает не более, чем в O (n точек полигона):
public static bool IsInConvexPolygon(Point testPoint, List<Point> polygon)
{
//Check if a triangle or higher n-gon
Debug.Assert(polygon.Length >= 3);
//n>2 Keep track of cross product sign changes
var pos = 0;
var neg = 0;
for (var i = 0; i < polygon.Count; i++)
{
//If point is in the polygon
if (polygon[i] == testPoint)
return true;
//Form a segment between the i'th point
var x1 = polygon[i].X;
var y1 = polygon[i].Y;
//And the i+1'th, or if i is the last, with the first point
var i2 = i < polygon.Count - 1 ? i + 1 : 0;
var x2 = polygon[i2].X;
var y2 = polygon[i2].Y;
var x = testPoint.X;
var y = testPoint.Y;
//Compute the cross product
var d = (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1);
if (d > 0) pos++;
if (d < 0) neg++;
//If the sign changes, then point is outside
if (pos > 0 && neg > 0)
return false;
}
//If no change in direction, then on same side of all segments, and thus inside
return true;
}
Ответ 4
Функция pointPolygonTest в openCV "определяет, находится ли точка внутри контура, снаружи или лежит на ребре": http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=pointpolygontest#pointpolygontest
Ответ 5
fortran ответ почти сработал у меня, но я обнаружил, что мне пришлось переводить многоугольник так, чтобы тестируемая точка была такой же, как и исходная. Вот JavaScript, который я написал, чтобы сделать эту работу:
function Vec2(x, y) {
return [x, y]
}
Vec2.nsub = function (v1, v2) {
return Vec2(v1[0]-v2[0], v1[1]-v2[1])
}
// aka the "scalar cross product"
Vec2.perpdot = function (v1, v2) {
return v1[0]*v2[1] - v1[1]*v2[0]
}
// Determine if a point is inside a polygon.
//
// point - A Vec2 (2-element Array).
// polyVerts - Array of Vec2 (2-element Arrays). The vertices that make
// up the polygon, in clockwise order around the polygon.
//
function coordsAreInside(point, polyVerts) {
var i, len, v1, v2, edge, x
// First translate the polygon so that `point` is the origin. Then, for each
// edge, get the angle between two vectors: 1) the edge vector and 2) the
// vector of the first vertex of the edge. If all of the angles are the same
// sign (which is negative since they will be counter-clockwise) then the
// point is inside the polygon; otherwise, the point is outside.
for (i = 0, len = polyVerts.length; i < len; i++) {
v1 = Vec2.nsub(polyVerts[i], point)
v2 = Vec2.nsub(polyVerts[i+1 > len-1 ? 0 : i+1], point)
edge = Vec2.nsub(v1, v2)
// Note that we could also do this by using the normal + dot product
x = Vec2.perpdot(edge, v1)
// If the point lies directly on an edge then count it as in the polygon
if (x < 0) { return false }
}
return true
}
Ответ 6
Или от человека, написавшего книгу, смотрите - геометрия страницы
В частности эта страница, он обсуждает, почему правило обмотки обычно лучше, чем пересечение лучей.
edit - Извините, что это не Jospeh O'Rourke, который написал отличную книгу "Вычислительная геометрия в C" , это Paul Bourke, но все же очень хороший источник алгоритмов геометрии.
Ответ 7
как я знаю, это что-то вроде этого.
вы выбираете точку где-то вне полигона, она может быть далеко от геометрии. то вы нарисуете линию с этой точки. я имею в виду, что вы создаете линейное уравнение с этими двумя точками.
то для каждой строки этого многоугольника вы проверяете, пересекаются ли они.
их сумма числа пересекающихся строк дает вам это внутри или нет.
если он нечетный: внутри
если он четный: вне