Как определить, находится ли точка справа или слева от линии

У меня есть набор точек. Я хочу разделить их на 2 отдельных набора. Для этого я выбираю два пункта (a и b) и провести воображаемую линию между ними. Теперь я хочу, чтобы все точки, которые остались от этой линии в одном наборе, и те, которые находятся справа от этой линии в другом наборе.

Как я могу сказать в любой момент z находится ли он в левом или в правом наборе? Я попытался вычислить угол между a-z-b – углы меньше 180 находятся на правой стороне, больше 180 – на левой стороне, но из-за определения ArcCos вычисленные углы всегда меньше 180°. Есть ли формула для расчета углов больше 180° (или любая другая формула для выбора правой или левой стороны)?

11 ответов


используйте знак определителя векторов (AB,AM), где M(X,Y) смысл запроса:

position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))

это 0, и +1 С одной стороны, -1 на другой стороне.


попробуйте этот код, который использует произведение:

public bool isLeft(Point a, Point b, Point c){
     return ((b.X - a.X)*(c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X)) > 0;
}

здесь a = точка линии 1; b = точка линии 2; c = точка для проверки.

если формула равна 0, точки коллинеарны.

Если линия горизонтальна, то это возвращает true, если точка находится выше линии.


вы смотрите на знак определителя

| x2-x1  x3-x1 |
| y2-y1  y3-y1 |

он будет положительным для точек с одной стороны и отрицательным с другой (и нулевым для точек на самой линии).


вектор (y1 - y2, x2 - x1) перпендикулярно линии и всегда указывает вправо (или всегда указывает влево, если вы плоскость ориентации отличается от моей).

затем вы можете вычислить скалярное произведение этого вектора и (x3 - x1, y3 - y1) чтобы определить, находится ли точка на той же стороне линии, что и перпендикулярный вектор (точечное произведение > 0) или нет.


я реализовал это на java и запустил модульный тест (источник ниже). Ни одно из вышеперечисленных решений не работает. Этот код проходит модульный тест. Если кто-нибудь найдет модульный тест, который не пройдет, пожалуйста, дайте мне знать.

код: Примечание: nearlyEqual(double,double) возвращает true, если два числа очень близки.

/*
 * @return integer code for which side of the line ab c is on.  1 means
 * left turn, -1 means right turn.  Returns
 * 0 if all three are on a line
 */
public static int findSide(
        double ax, double ay, 
        double bx, double by,
        double cx, double cy) {
    if (nearlyEqual(bx-ax,0)) { // vertical line
        if (cx < bx) {
            return by > ay ? 1 : -1;
        }
        if (cx > bx) {
            return by > ay ? -1 : 1;
        } 
        return 0;
    }
    if (nearlyEqual(by-ay,0)) { // horizontal line
        if (cy < by) {
            return bx > ax ? -1 : 1;
        }
        if (cy > by) {
            return bx > ax ? 1 : -1;
        } 
        return 0;
    }
    double slope = (by - ay) / (bx - ax);
    double yIntercept = ay - ax * slope;
    double cSolution = (slope*cx) + yIntercept;
    if (slope != 0) {
        if (cy > cSolution) {
            return bx > ax ? 1 : -1;
        }
        if (cy < cSolution) {
            return bx > ax ? -1 : 1;
        }
        return 0;
    }
    return 0;
}

вот модульный тест:

@Test public void testFindSide() {
    assertTrue("1", 1 == Utility.findSide(1, 0, 0, 0, -1, -1));
    assertTrue("1.1", 1 == Utility.findSide(25, 0, 0, 0, -1, -14));
    assertTrue("1.2", 1 == Utility.findSide(25, 20, 0, 20, -1, 6));
    assertTrue("1.3", 1 == Utility.findSide(24, 20, -1, 20, -2, 6));

    assertTrue("-1", -1 == Utility.findSide(1, 0, 0, 0, 1, 1));
    assertTrue("-1.1", -1 == Utility.findSide(12, 0, 0, 0, 2, 1));
    assertTrue("-1.2", -1 == Utility.findSide(-25, 0, 0, 0, -1, -14));
    assertTrue("-1.3", -1 == Utility.findSide(1, 0.5, 0, 0, 1, 1));

    assertTrue("2.1", -1 == Utility.findSide(0,5, 1,10, 10,20));
    assertTrue("2.2", 1 == Utility.findSide(0,9.1, 1,10, 10,20));
    assertTrue("2.3", -1 == Utility.findSide(0,5, 1,10, 20,10));
    assertTrue("2.4", -1 == Utility.findSide(0,9.1, 1,10, 20,10));

    assertTrue("vertical 1", 1 == Utility.findSide(1,1, 1,10, 0,0));
    assertTrue("vertical 2", -1 == Utility.findSide(1,10, 1,1, 0,0));
    assertTrue("vertical 3", -1 == Utility.findSide(1,1, 1,10, 5,0));
    assertTrue("vertical 3", 1 == Utility.findSide(1,10, 1,1, 5,0));

    assertTrue("horizontal 1", 1 == Utility.findSide(1,-1, 10,-1, 0,0));
    assertTrue("horizontal 2", -1 == Utility.findSide(10,-1, 1,-1, 0,0));
    assertTrue("horizontal 3", -1 == Utility.findSide(1,-1, 10,-1, 0,-9));
    assertTrue("horizontal 4", 1 == Utility.findSide(10,-1, 1,-1, 0,-9));

    assertTrue("positive slope 1", 1 == Utility.findSide(0,0, 10,10, 1,2));
    assertTrue("positive slope 2", -1 == Utility.findSide(10,10, 0,0, 1,2));
    assertTrue("positive slope 3", -1 == Utility.findSide(0,0, 10,10, 1,0));
    assertTrue("positive slope 4", 1 == Utility.findSide(10,10, 0,0, 1,0));

    assertTrue("negative slope 1", -1 == Utility.findSide(0,0, -10,10, 1,2));
    assertTrue("negative slope 2", -1 == Utility.findSide(0,0, -10,10, 1,2));
    assertTrue("negative slope 3", 1 == Utility.findSide(0,0, -10,10, -1,-2));
    assertTrue("negative slope 4", -1 == Utility.findSide(-10,10, 0,0, -1,-2));

    assertTrue("0", 0 == Utility.findSide(1, 0, 0, 0, -1, 0));
    assertTrue("1", 0 == Utility.findSide(0,0, 0, 0, 0, 0));
    assertTrue("2", 0 == Utility.findSide(0,0, 0,1, 0,2));
    assertTrue("3", 0 == Utility.findSide(0,0, 2,0, 1,0));
    assertTrue("4", 0 == Utility.findSide(1, -2, 0, 0, -1, 2));
}

с помощью уравнение линии ab, получите координату x на линии в той же координате y, что и сортируемая точка.

  • если точка x > линия x, точка находится справа от линии.
  • Если точки x
  • если точка x = = линия x, точка находится на линии.

сначала проверьте, есть ли у вас вертикальная линия:

if (x2-x1) == 0
  if x3 < x2
     it's on the left
  if x3 > x2
     it's on the right
  else
     it's on the line

затем вычислите наклон:m = (y2-y1)/(x2-x1)

затем создайте уравнение линии, используя форму наклона точки:y - y1 = m*(x-x1) + y1. Для моего объяснения упростите его до формы наклона-перехвата (не обязательно в вашем алгоритме):y = mx+b.

теперь подключите (x3, y3) на x и y. Вот некоторый псевдокод, детализирующий, что должно произойти:

if m > 0
  if y3 > m*x3 + b
    it's on the left
  else if y3 < m*x3 + b
    it's on the right
  else
    it's on the line
else if m < 0
  if y3 < m*x3 + b
    it's on the left
  if y3 > m*x3+b
    it's on the right
  else
    it's on the line
else
  horizontal line; up to you what you do

в принципе, я думаю, что есть решение, которое намного проще и прямолинейно,для любого заданного многоугольника,скажем,состоит из четырех вершин(p1, p2, p3, p4), найти две крайние противоположные вершины в многоугольнике, другими словами, найти, например, самую верхнюю левую вершину (скажем, p1) и противоположную вершину, которая находится в самом нижнем правом углу (скажем ). Следовательно, учитывая вашу тестовую точку C( x, y), Теперь вам нужно сделать двойную проверку между C и p1 и C и p4:

Если cx > p1x и cy > p1y ==> означает, что C ниже и справа от p1 следующий если cx означает, что C находится вверху и слева от p4

заключение, C находится внутри прямоугольника.

спасибо :)


предполагая, что точки (Ax,Ay) (Bx,By) и (Cx, Cy), вам нужно вычислить:

(Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx-Ax)

Это будет равно нулю, если точка C находится на линии, образованной точками A и B, и будет иметь другой знак в зависимости от стороны. Какая сторона это зависит от ориентации ваших координат (x, y), но вы можете подключить тестовые значения для A, B и C в эту формулу, чтобы определить, являются ли отрицательные значения слева или право.


ответ@AVB в ruby

det = Matrix[
  [(x2 - x1), (x3 - x1)],
  [(y2 - y1), (y3 - y1)]
].determinant

Если det положительно ее выше, если отрицательно ее ниже. Если 0, он на линии.


вот версия, снова используя логику кросс-продукта, написанную на Clojure.

(defn is-left? [line point]
  (let [[[x1 y1] [x2 y2]] (sort line)
        [x-pt y-pt] point]
    (> (* (- x2 x1) (- y-pt y1)) (* (- y2 y1) (- x-pt x1)))))

пример использования:

(is-left? [[-3 -1] [3 1]] [0 10])
true

то есть точка (0, 10) находится слева от линии, определяемой (-3, -1) и (3, 1).

Примечание: эта реализация решает проблему, которую никто из других (до сих пор) не делает! порядок имеет значение при задании точек, определяющих линию. То есть, это "направленная линия", в определенном смысле. Так с приведенный выше код, этот вызов также дает результат true:

(is-left? [[3 1] [-3 -1]] [0 10])
true

это из-за этого фрагмента кода:

(sort line)

наконец, как и в других решениях на основе кросс-продуктов, это решение возвращает логическое значение и не дает третьего результата для коллинеарности. Но это даст результат, который имеет смысл, например:

(is-left? [[1 1] [3 1]] [10 1])
false