Как определить, находится ли точка справа или слева от линии
У меня есть набор точек. Я хочу разделить их на 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