Как определить, находится ли точка внутри двумерного выпуклого многоугольника?

У меня выпуклый многоугольник (обычно только повернутый квадрат), и я знаю все 4 точки. Как определить, находится ли данная точка (желтый/зеленый) внутри многоугольника?

enter image description here

EDIT: для этого конкретного проекта у меня нет доступа ко всем библиотекам JDK, таким как AWT.

Ответ 1

На этой странице: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html показано, как это сделать для любого многоугольника.

У меня есть реализация этого на Java, но она слишком велика, чтобы размещать здесь ее полностью. Тем не менее, вы должны быть в состоянии решить это:

class Boundary {
    private final Point[] points; // Points making up the boundary
    ...


    /**
     * Return true if the given point is contained inside the boundary.
     * See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
     * @param test The point to check
     * @return true if the point is inside the boundary, false otherwise
     *
     */
    public boolean contains(Point test) {
      int i;
      int j;
      boolean result = false;
      for (i = 0, j = points.length - 1; i < points.length; j = i++) {
        if ((points[i].y > test.y) != (points[j].y > test.y) &&
            (test.x < (points[j].x - points[i].x) * (test.y - points[i].y) / (points[j].y-points[i].y) + points[i].x)) {
          result = !result;
         }
      }
      return result;
    }
}

А вот и эскиз класса Point

/**
 * Two dimensional cartesian point.
 */
public class Point {
  public final double x;
  public final double y;
  ...
}

Ответ 2

Для тех, кто хотел бы понять, как работает метод, написанный Дином Повеем, вот объяснение:

Метод смотрит на "луч", который начинается с тестируемого пятна и простирается до бесконечности в правую сторону оси X. Для каждого сегмента многоугольника он проверяет, пересекает ли луч. Если общее число пересечений сегментов нечетно, то проверенная точка рассматривается внутри многоугольника, в противном случае - снаружи.

Чтобы понять, как рассчитывается пересечение, рассмотрим следующий рисунок:

            v2
            o
           /
          / c (intersection)
o--------x----------------------> to infinity
t       /
       /   
      /
     o
     v1

Для того, чтобы пересечение происходило, test.y должно находиться между значениями y вершин сегмента (v1 и v2). Это первое условие оператора if в методе. Если это то, что происходит, то горизонтальная линия должна пересекать сегмент. Остается только установить, происходит ли пересечение справа от тестируемой точки или слева от нее. Для этого требуется найти координату x точки пересечения, которая равна:

              t.y - v1.y
c.x = v1.x + ----------- * (v2.x - v1.x)
             v2.y - v1.y

Все, что еще предстоит сделать, - это изучение тонкостей:

  • Если v1.y == v2.y, тогда луч идет по отрезку и поэтому сегмент не влияет на результат. Действительно, первая часть оператора if возвращает false в этом случае.
  • Сначала код умножается и только потом делит. Это делается для поддержки очень небольшие различия между v1.x и v2.x, которые может привести к нулю после деления из-за округления.
  • Наконец, проблема пересечения точно на вершине должна быть адресованный. Рассмотрим следующие два случая:
         o                    o
         |                     \     o
         | A1                C1 \   /
         |                       \ / C2  
o--------x-----------x------------x--------> to infinity
        /           / \
    A2 /        B1 /   \ B2
      /           /     \ 
     o           /       o
                o

Теперь, чтобы проверить, работает ли он, проверьте сами, что возвращается для каждого из 4 сегментов по условию if в теле метода. Вы должны обнаружить, что сегменты выше луча (A1, C1, C2) получают положительный результат, тогда как те, которые ниже него (A2, B1, B2) получают отрицательный один. Это означает, что вершина A вносит нечетное число (1) в пересечение count, а B и C - четное число (0 и 2 соответственно), что это именно то, что нужно. A действительно является реальным пересечением многоугольника, тогда как B и C - всего лишь два случая "пролета".

Ответ 3

Предполагая, что ваша точка находится в координате Y y, просто вычислите позиции x, где каждая многоугольных (не горизонтальных) линий пересекают y. Подсчитайте количество позиций x, которые меньше, чем позиция x вашей точки. Если число позиций x нечетное, ваша точка внутри многоугольника. Примечание: это работает для всех полигонов, а не только выпуклых. Подумайте об этом так: нарисуйте линию от бесконечно далеко прямо до своей точки. Когда эта линия пересекает polygon line, теперь он находится внутри многоугольника. Пересеките линию снова, снаружи. Крест снова, внутри (и т.д.). Надеюсь, это поможет!

Ответ 4

Класс java.awt.Polygon имеет несколько методов contains(...), если вы используете объекты Polygon для представления вашего многоугольника.

Ответ 5

Просто добавьте (простую) реализацию Java исходного кода в C из код, предложенный @Dean Povey (я не знаю почему @Dean Povey ссылается на большую реализацию):

static boolean pnpoly(double[] vertx, double[] verty, double testx, double testy)
{
    int nvert = vertx.length;
    int i, j;
    boolean c = false;
    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        if ( ((verty[i]>testy) != (verty[j]>testy)) &&
                (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
            c = !c;
    }
    return c;
}   

Я не изменил случай, чтобы соответствовать правилу Java, чтобы показать минимальные требуемые изменения. Я также проверил его в простых случаях, и он отлично работает.

Ответ 6

Проверьте, находится ли он на одной стороне 4 полуплоскостей, определенных линиями, которые содержат сегменты линии, составляющие стороны квада.

Здесь является хорошим объяснением.

Ответ 7

Скажем, x [] - это массив x-точек, а y [] - массив y-точек.
Вы должны вернуть 1, если точка существует в многоугольнике, а 2 - нет. где (плоскость X, плоскость Y) - это точка, которую вы должны проверить.

//check like this
return new Polygon(x,y,x.length).contains(planeX, planeY)?1:2;

Ответ 8

x_array: Array[Integer] многоугольника x_array: Array[Integer]

y_array: Array[Integer] многоугольника: y_array: Array[Integer]

Точка: x: Integer, y: Integer

import java.awt.Polygon
import java.awt.Point
...

final boolean isInPolygon = 
    new Polygon(x_array,y_array,x_array.length).contains(new Point(x, y));

В этом примере мы создаем объект java.awt.Polygon и используем метод содержит, чтобы проверить, находятся ли ваши координаты в форме, которую вы разработали.

Я использую объект java.awt.Point для представления координат, чтобы сделать код элегантным, но это необязательно, вы можете напрямую использовать .contains(x, y)