Как я могу проверить, пересекаются ли 2 сегмента?
У меня есть следующие данные:
Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ]
Мне нужно написать небольшой алгоритм на Python, чтобы определить, пересекаются ли две линии.
Как я могу проверить, пересекаются ли 2 сегмента?
У меня есть следующие данные:
Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ]
Мне нужно написать небольшой алгоритм на Python, чтобы определить, пересекаются ли две линии.
Уравнение прямой:
f(x) = A*x + b = y
Для сегмента это точно так же, за исключением того, что x входит в интервал I.
Если у вас есть два сегмента, определенные следующим образом:
Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}
Абсцесс Xa потенциальной точки пересечения (Xa, Ya) должен содержаться в обоих интервалах I1 и I2, определяемых следующим образом:
I1 = [min(X1,X2), max(X1,X2)]
I2 = [min(X3,X4), max(X3,X4)]
И мы могли бы сказать, что Ха входит в:
Ia = [max( min(X1,X2), min(X3,X4) ),
min( max(X1,X2), max(X3,X4) )]
Теперь нам нужно проверить, существует ли этот интервал Ia:
if (max(X1,X2) < min(X3,X4))
return false; // There is no mutual abcisses
Итак, у нас есть две строки формулы и взаимный интервал. Ваши формулы линии:
f1(x) = A1*x + b1 = y
f2(x) = A2*x + b2 = y
Поскольку мы получили две точки по сегментам, мы можем определить A1, A2, b1 и b2:
A1 = (Y1-Y2)/(X1-X2) // Pay attention to not dividing by zero
A2 = (Y3-Y4)/(X3-X4) // Pay attention to not dividing by zero
b1 = Y1-A1*X1 = Y2-A1*X2
b2 = Y3-A2*X3 = Y4-A2*X4
Если сегменты параллельны, то A1 == A2:
if (A1 == A2)
return false; // Parallel segments
Точка (Xa, Ya), стоящая на обеих линиях, должна проверять обе формулы f1 и f2:
Ya = A1 * Xa + b1
Ya = A2 * Xa + b2
A1 * Xa + b1 = A2 * Xa + b2
Xa = (b2 - b1) / (A1 - A2) // Once again, pay attention to not dividing by zero
Последнее, что нужно сделать, это проверить, что Xa входит в Ia:
if ( (Xa < max( min(X1,X2), min(X3,X4) )) ||
(Xa > min( max(X1,X2), max(X3,X4) )) )
return false; // intersection is out of bound
else
return true;
В дополнение к этому, вы можете проверить при запуске, что две из четырех предоставленных точек не равны, чтобы избежать всего этого тестирования.
Пользователь @i_4_got указывает на эту страницу с очень эффективным решением в Python. Я воспроизвожу его здесь для удобства (так как это сделало бы меня счастливым иметь его здесь):
def ccw(A,B,C):
return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x)
# Return true if line segments AB and CD intersect
def intersect(A,B,C,D):
return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)
Вам не нужно точно вычислять, где пересекаются сегменты, а только понимать , пересекаются ли они вообще. Это упростит решение.
Идея состоит в том, чтобы рассматривать один сегмент как "якорь" и разделять второй сегмент на 2 точки.
Теперь вам нужно найти относительное положение каждой точки для "привязанного" сегмента (OnLeft, OnRight или Collinear).
После этого для обеих точек убедитесь, что одна из точек - OnLeft, а другая - OnRight (или, возможно, включите коллинеарную позицию, если вы также хотите включить неправильные пересечения).
Затем вы должны повторить процесс с ролями якоря и отдельных сегментов.
Пересечение существует тогда и только тогда, когда одной из точек является OnLeft, а другой - OnRight. Смотрите эту ссылку для более подробного объяснения с примерами изображений для каждого возможного случая.
Реализация такого метода будет намного проще, чем реализация метода, который находит точку пересечения (учитывая множество угловых случаев, которые вам также придется обрабатывать).
Обновить
Следующие функции должны проиллюстрировать идею (источник: вычислительная геометрия в C).
Примечание. В этом примере предполагается использование целых чисел. Если вместо этого вы используете некоторое представление с плавающей запятой (которое, очевидно, может усложнить ситуацию), вам следует определить некоторое значение эпсилона, чтобы указать "равенство" (в основном для оценки IsCollinear
).
// points "a" and "b" forms the anchored segment.
// point "c" is the evaluated point
bool IsOnLeft(Point a, Point b, Point c)
{
return Area2(a, b, c) > 0;
}
bool IsOnRight(Point a, Point b, Point c)
{
return Area2(a, b, c) < 0;
}
bool IsCollinear(Point a, Point b, Point c)
{
return Area2(a, b, c) == 0;
}
// calculates the triangle size (formed by the "anchor" segment and additional point)
int Area2(Point a, Point b, Point c)
{
return (b.X - a.X) * (c.Y - a.Y) -
(c.X - a.X) * (b.Y - a.Y);
}
Конечно, при использовании этих функций необходимо помнить, что каждый сегмент лежит "между" другим сегментом (поскольку это конечные сегменты, а не бесконечные линии).
Кроме того, используя эти функции, вы можете понять, есть ли у вас правильное или неправильное пересечение.
Предположим, что два сегмента имеют конечные точки A, B и C, D. Численно надежным способом определения пересечения является проверка знака четырех определяющих факторов:
| Ax-Cx Bx-Cx | | Ax-Dx Bx-Dx |
| Ay-Cy By-Cy | | Ay-Dy By-Dy |
| Cx-Ax Dx-Ax | | Cx-Bx Dx-Bx |
| Cy-Ay Dy-Ay | | Cy-By Dy-By |
Для пересечения каждый детерминант слева должен иметь знак противоположности справа, но между двумя линиями не должно быть никакого отношения. Вы в основном проверяете каждую точку сегмента на другой сегмент, чтобы убедиться, что они лежат на противоположных сторонах линии, определенной другим сегментом.
Смотрите здесь: http://www.cs.cmu.edu/~quake/robust.html
На основе Liran's и отличных ответов Grumdrig's здесь полный код Python для проверки того, замкнутые сегменты пересекаются. Работает для коллинеарных сегментов, сегментов, параллельных оси Y, вырожденных отрезков (дьявол подробно). Предполагает целочисленные координаты. Координаты с плавающей точкой требуют модификации теста равенства точек.
def side(a,b,c):
""" Returns a position of the point c relative to the line going through a and b
Points a, b are expected to be different
"""
d = (c[1]-a[1])*(b[0]-a[0]) - (b[1]-a[1])*(c[0]-a[0])
return 1 if d > 0 else (-1 if d < 0 else 0)
def is_point_in_closed_segment(a, b, c):
""" Returns True if c is inside closed segment, False otherwise.
a, b, c are expected to be collinear
"""
if a[0] < b[0]:
return a[0] <= c[0] and c[0] <= b[0]
if b[0] < a[0]:
return b[0] <= c[0] and c[0] <= a[0]
if a[1] < b[1]:
return a[1] <= c[1] and c[1] <= b[1]
if b[1] < a[1]:
return b[1] <= c[1] and c[1] <= a[1]
return a[0] == c[0] and a[1] == c[1]
#
def closed_segment_intersect(a,b,c,d):
""" Verifies if closed segments a, b, c, d do intersect.
"""
if a == b:
return a == c or a == d
if c == d:
return c == a or c == b
s1 = side(a,b,c)
s2 = side(a,b,d)
# All points are collinear
if s1 == 0 and s2 == 0:
return \
is_point_in_closed_segment(a, b, c) or is_point_in_closed_segment(a, b, d) or \
is_point_in_closed_segment(c, d, a) or is_point_in_closed_segment(c, d, b)
# No touching and on the same side
if s1 and s1 == s2:
return False
s1 = side(c,d,a)
s2 = side(c,d,b)
# No touching and on the same side
if s1 and s1 == s2:
return False
return True
У вас есть два сегмента строк. Определите один сегмент с помощью конечных точек A и B и второго сегмента конечными точками C и D. Существует хороший трюк, чтобы показать, что они должны пересекаться, В пределах границ сегментов. (Обратите внимание, что сами строки могут пересекаться за пределами сегментов, поэтому вы должны быть осторожны. Хороший код также будет наблюдать за параллельными линиями.)
Трюк состоит в том, чтобы проверить, что точки A и B должны проходить на противоположных сторонах линии CD, а точки C и D должны лежать на противоположных сторонах линии AB.
Так как это домашнее задание, я не дам вам явного решения. Но простой тест, чтобы увидеть, на какую сторону линии находится точка, - это использовать точечный продукт. Таким образом, для данной строки CD вычислите нормальный вектор этой линии (я назову его N_C.) Теперь просто проверьте признаки этих двух результатов:
dot(A-C,N_C)
и
dot(B-C,N_C)
Если эти результаты имеют противоположные знаки, то A и B являются противоположными сторонами линии CD. Теперь сделаем тот же тест для другой линии, AB. Он имеет нормальный вектор N_A. Сравните признаки
dot(C-A,N_A)
и
dot(D-A,N_A)
Я оставлю это вам, чтобы выяснить, как вычислить нормальный вектор. (В 2-й, это тривиально, но будет ли ваш код беспокоиться о том, являются ли A и B отличными точками? Аналогично, C и D отличаются?)
Вам все равно нужно беспокоиться о сегментах линии, которые лежат вдоль одной и той же бесконечной линии, или если одна точка фактически попадает на другой сегмент линии. Хороший код будет отвечать любой возможной проблеме.
Вот код C, чтобы проверить, находятся ли две точки на противоположных сторонах отрезка. Используя этот код, вы можете проверить, пересекаются и два сегмента.
// true if points p1, p2 lie on the opposite sides of segment s1--s2
bool oppositeSide (Point2f s1, Point2f s2, Point2f p1, Point2f p2) {
//calculate normal to the segment
Point2f vec = s1-s2;
Point2f normal(vec.y, -vec.x); // no need to normalize
// vectors to the points
Point2f v1 = p1-s1;
Point2f v2 = p2-s1;
// compare signs of the projections of v1, v2 onto the normal
float proj1 = v1.dot(normal);
float proj2 = v2.dot(normal);
if (proj1==0 || proj2==0)
cout<<"collinear points"<<endl;
return(SIGN(proj1) != SIGN(proj2));
}
Вот еще один код Python, чтобы проверить, пересекаются ли замкнутые сегменты. Это переписанная версия кода C++ в http://www.cdn.geeksforgeeks.org/check-if-two-given-line-segments-intersect/. Эта реализация охватывает все особые случаи (например, все коллинеарные точки).
def on_segment(p, q, r):
'''Given three colinear points p, q, r, the function checks if
point q lies on line segment "pr"
'''
if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and
q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])):
return True
return False
def orientation(p, q, r):
'''Find orientation of ordered triplet (p, q, r).
The function returns following values
0 --> p, q and r are colinear
1 --> Clockwise
2 --> Counterclockwise
'''
val = ((q[1] - p[1]) * (r[0] - q[0]) -
(q[0] - p[0]) * (r[1] - q[1]))
if val == 0:
return 0 # colinear
elif val > 0:
return 1 # clockwise
else:
return 2 # counter-clockwise
def do_intersect(p1, q1, p2, q2):
'''Main function to check whether the closed line segments p1 - q1 and p2
- q2 intersect'''
o1 = orientation(p1, q1, p2)
o2 = orientation(p1, q1, q2)
o3 = orientation(p2, q2, p1)
o4 = orientation(p2, q2, q1)
# General case
if (o1 != o2 and o3 != o4):
return True
# Special Cases
# p1, q1 and p2 are colinear and p2 lies on segment p1q1
if (o1 == 0 and on_segment(p1, p2, q1)):
return True
# p1, q1 and p2 are colinear and q2 lies on segment p1q1
if (o2 == 0 and on_segment(p1, q2, q1)):
return True
# p2, q2 and p1 are colinear and p1 lies on segment p2q2
if (o3 == 0 and on_segment(p2, p1, q2)):
return True
# p2, q2 and q1 are colinear and q1 lies on segment p2q2
if (o4 == 0 and on_segment(p2, q1, q2)):
return True
return False # Doesn't fall in any of the above cases
Ниже приведена тестовая функция, чтобы убедиться, что она работает.
import matplotlib.pyplot as plt
def test_intersect_func():
p1 = (1, 1)
q1 = (10, 1)
p2 = (1, 2)
q2 = (10, 2)
fig, ax = plt.subplots()
ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
print(do_intersect(p1, q1, p2, q2))
p1 = (10, 0)
q1 = (0, 10)
p2 = (0, 0)
q2 = (10, 10)
fig, ax = plt.subplots()
ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
print(do_intersect(p1, q1, p2, q2))
p1 = (-5, -5)
q1 = (0, 0)
p2 = (1, 1)
q2 = (10, 10)
fig, ax = plt.subplots()
ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
print(do_intersect(p1, q1, p2, q2))
p1 = (0, 0)
q1 = (1, 1)
p2 = (1, 1)
q2 = (10, 10)
fig, ax = plt.subplots()
ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
print(do_intersect(p1, q1, p2, q2))
для сегментов AB и CD, найдите наклон CD
slope=(Dy-Cy)/(Dx-Cx)
расширьте CD над A и B и возьмите расстояние до CD, идущего прямо вверх
dist1=slope*(Cx-Ax)+Ay-Cy
dist2=slope*(Dx-Ax)+Ay-Dy
проверьте, находятся ли они на противоположных сторонах
return dist1*dist2<0
Так как вы не говорите, что хотите найти точку пересечения линии, проблема будет проще решить. Если вам нужна точка пересечения, то ответ OMG_peanuts - это более быстрый подход. Однако, если вы просто хотите найти, пересекаются ли линии или нет, вы можете сделать это, используя линейное уравнение (ax + by + c = 0). Этот подход выглядит следующим образом:
Начните с двух сегментов: сегмент 1 и сегмент 2.
segment1 = [[x1,y1], [x2,y2]]
segment2 = [[x3,y3], [x4,y4]]
Проверьте, являются ли два сегмента линии не нулевой линией длины и отдельными сегментами.
Отсюда я полагаю, что два сегмента имеют ненулевую длину и различны. Для каждого сегмента линии вычислим наклон линии, а затем получим уравнение линии в виде ax + на + c = 0. Теперь вычислим значение f = ax + на + c для двух точек другой сегмент линии (повторите это и для другого сегмента линии).
a2 = (y3-y4)/(x3-x4);
b1 = -1;
b2 = -1;
c1 = y1 - a1*x1;
c2 = y3 - a2*x3;
// using the sign function from numpy
f1_1 = sign(a1*x3 + b1*y3 + c1);
f1_2 = sign(a1*x4 + b1*y4 + c1);
f2_1 = sign(a2*x1 + b2*y1 + c2);
f2_2 = sign(a2*x2 + b2*y2 + c2);
Теперь все, что осталось, это разные случаи. Если f = 0 для любой точки, то две линии касаются точки. Если f1_1 и f1_2 равны или f2_1 и f2_2 равны, то линии не пересекаются. Если f1_1 и f1_2 не равны и f2_1, а f2_2 неравны, то сегменты линии пересекаются. В зависимости от того, хотите ли вы рассматривать линии, которые касаются "пересекающихся" или нет, вы можете адаптировать свои условия.
Мы также можем решить эту проблему, используя векторы.
Определим сегменты как [start, end]
. Учитывая два таких сегмента [A, B]
и [C, D]
, что обе имеют отличную от нуля длину, мы можем выбрать одну из конечных точек, которые будут использоваться в качестве контрольной точки, чтобы мы получили три вектора:
x = 0
y = 1
p = A-C = [C[x]-A[x], C[y]-A[y]]
q = B-A = [B[x]-A[x], B[y]-A[y]]
r = D-C = [D[x]-C[x], D[y]-C[y]]
Оттуда мы можем искать пересечение, вычисляя t и u в p + t*r = u*q
. Немного поиграв с уравнением, получим:
t = (q[y]*p[x] - q[x]*p[y])/(q[x]*r[y] - q[y]*r[x])
u = (p[x] + t*r[x])/q[x]
Таким образом, функция:
def intersects(a, b):
p = [b[0][0]-a[0][0], b[0][1]-a[0][1]]
q = [a[1][0]-a[0][0], a[1][1]-a[0][1]]
r = [b[1][0]-b[0][0], b[1][1]-b[0][1]]
t = (q[1]*p[0] - q[0]*p[1])/(q[0]*r[1] - q[1]*r[0]) \
if (q[0]*r[1] - q[1]*r[0]) != 0 \
else (q[1]*p[0] - q[0]*p[1])
u = (p[0] + t*r[0])/q[0] \
if q[0] != 0 \
else (p[1] + t*r[1])/q[1]
return t >= 0 and t <= 1 and u >= 0 and u <= 1
Если ваши данные определяют строку, вам просто нужно доказать, что они не параллельны. Для этого вы можете вычислить
alpha = float(y2 - y1) / (x2 - x1).
Если этот коэффициент равен как для Line1, так и для Line2, это означает, что линия параллельна. Если нет, значит, они пересекаются.
Если они параллельны, вам нужно доказать, что они не совпадают. Для этого вы вычисляете
beta = y1 - alpha*x1
Если бета одинакова для Line1 и Line2, это означает, что вы пересекаете линию, поскольку они равны
Если это сегмент, вам все равно придется вычислять альфа и бета, как описано выше для каждой строки. Затем вам нужно проверить, что (beta1 - beta2)/(alpha1 - alpha2) больше Min (x1_line1, x2_line1) и меньше Max (x1_line1, x2_line1)
Рассчитайте точку пересечения линий, лежащих на ваших сегментах (это означает в основном решение системы линейных уравнений), а затем проверьте, находится ли она между начальной и конечной точками ваших сегментов.
Это то, что у меня есть для AS3, не знаю много о питоне, но концепция там
public function getIntersectingPointF($A:Point, $B:Point, $C:Point, $D:Point):Number {
var A:Point = $A.clone();
var B:Point = $B.clone();
var C:Point = $C.clone();
var D:Point = $D.clone();
var f_ab:Number = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x);
// are lines parallel
if (f_ab == 0) { return Infinity };
var f_cd:Number = (B.x - A.x) * (A.y - C.y) - (B.y - A.y) * (A.x - C.x);
var f_d:Number = (D.y - C.y) * (B.x - A.x) - (D.x - C.x) * (B.y - A.y);
var f1:Number = f_ab/f_d
var f2:Number = f_cd / f_d
if (f1 == Infinity || f1 <= 0 || f1 >= 1) { return Infinity };
if (f2 == Infinity || f2 <= 0 || f2 >= 1) { return Infinity };
return f1;
}
public function getIntersectingPoint($A:Point, $B:Point, $C:Point, $D:Point):Point
{
var f:Number = getIntersectingPointF($A, $B, $C, $D);
if (f == Infinity || f <= 0 || f >= 1) { return null };
var retPoint:Point = Point.interpolate($A, $B, 1 - f);
return retPoint.clone();
}
Реализовано в JAVA. Однако кажется, что он не работает для колинейных линий (а также линейных сегментов, которые существуют внутри друг друга L1 (0,0) (10,10) L2 (1,1) (2,2)
public class TestCode
{
public class Point
{
public double x = 0;
public double y = 0;
public Point(){}
}
public class Line
{
public Point p1, p2;
public Line( double x1, double y1, double x2, double y2)
{
p1 = new Point();
p2 = new Point();
p1.x = x1;
p1.y = y1;
p2.x = x2;
p2.y = y2;
}
}
//line segments
private static Line s1;
private static Line s2;
public TestCode()
{
s1 = new Line(0,0,0,10);
s2 = new Line(-1,0,0,10);
}
public TestCode(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4)
{
s1 = new Line(x1,y1, x2,y2);
s2 = new Line(x3,y3, x4,y4);
}
public static void main(String args[])
{
TestCode code = null;
////////////////////////////
code = new TestCode(0,0,0,10,
0,1,0,5);
if( intersect(code) )
{ System.out.println( "OK COLINEAR: INTERSECTS" ); }
else
{ System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,0,10,
0,1,0,10);
if( intersect(code) )
{ System.out.println( "OK COLINEAR: INTERSECTS" ); }
else
{ System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,10,0,
5,0,15,0);
if( intersect(code) )
{ System.out.println( "OK COLINEAR: INTERSECTS" ); }
else
{ System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,10,0,
0,0,15,0);
if( intersect(code) )
{ System.out.println( "OK COLINEAR: INTERSECTS" ); }
else
{ System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,10,10,
1,1,5,5);
if( intersect(code) )
{ System.out.println( "OK COLINEAR: INTERSECTS" ); }
else
{ System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,0,10,
-1,-1,0,10);
if( intersect(code) )
{ System.out.println( "OK SLOPE END: INTERSECTS" ); }
else
{ System.out.println( "ERROR SLOPE END: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(-10,-10,10,10,
-10,10,10,-10);
if( intersect(code) )
{ System.out.println( "OK SLOPE Intersect(0,0): INTERSECTS" ); }
else
{ System.out.println( "ERROR SLOPE Intersect(0,0): DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(-10,-10,10,10,
-3,-2,50,-2);
if( intersect(code) )
{ System.out.println( "OK SLOPE Line2 VERTIAL: INTERSECTS" ); }
else
{ System.out.println( "ERROR SLOPE Line2 VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(-10,-10,10,10,
50,-2,-3,-2);
if( intersect(code) )
{ System.out.println( "OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS" ); }
else
{ System.out.println( "ERROR SLOPE Line2 (reversed) VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,0,10,
1,0,1,10);
if( intersect(code) )
{ System.out.println( "ERROR PARALLEL VERTICAL: INTERSECTS" ); }
else
{ System.out.println( "OK PARALLEL VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,2,10,2,
0,10,10,10);
if( intersect(code) )
{ System.out.println( "ERROR PARALLEL HORIZONTAL: INTERSECTS" ); }
else
{ System.out.println( "OK PARALLEL HORIZONTAL: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,10,5,13.75,
0,18.75,10,15);
if( intersect(code) )
{ System.out.println( "ERROR PARALLEL SLOPE=.75: INTERSECTS" ); }
else
{ System.out.println( "OK PARALLEL SLOPE=.75: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,1,1,
2,-1,2,10);
if( intersect(code) )
{ System.out.println( "ERROR SEPERATE SEGMENTS: INTERSECTS" ); }
else
{ System.out.println( "OK SEPERATE SEGMENTS: DO NOT INTERSECT" ); }
////////////////////////////
code = new TestCode(0,0,1,1,
-1,-10,-5,10);
if( intersect(code) )
{ System.out.println( "ERROR SEPERATE SEGMENTS 2: INTERSECTS" ); }
else
{ System.out.println( "OK SEPERATE SEGMENTS 2: DO NOT INTERSECT" ); }
}
public static boolean intersect( TestCode code )
{
return intersect( code.s1, code.s2);
}
public static boolean intersect( Line line1, Line line2 )
{
double i1min = Math.min(line1.p1.x, line1.p2.x);
double i1max = Math.max(line1.p1.x, line1.p2.x);
double i2min = Math.min(line2.p1.x, line2.p2.x);
double i2max = Math.max(line2.p1.x, line2.p2.x);
double iamax = Math.max(i1min, i2min);
double iamin = Math.min(i1max, i2max);
if( Math.max(line1.p1.x, line1.p2.x) < Math.min(line2.p1.x, line2.p2.x) )
return false;
double m1 = (line1.p2.y - line1.p1.y) / (line1.p2.x - line1.p1.x );
double m2 = (line2.p2.y - line2.p1.y) / (line2.p2.x - line2.p1.x );
if( m1 == m2 )
return false;
//b1 = line1[0][1] - m1 * line1[0][0]
//b2 = line2[0][1] - m2 * line2[0][0]
double b1 = line1.p1.y - m1 * line1.p1.x;
double b2 = line2.p1.y - m2 * line2.p1.x;
double x1 = (b2 - b1) / (m1 - m2);
if( (x1 < Math.max(i1min, i2min)) || (x1 > Math.min(i1max, i2max)) )
return false;
return true;
}
}
До сих пор выход
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
OK SLOPE END: INTERSECTS
OK SLOPE Intersect(0,0): INTERSECTS
OK SLOPE Line2 VERTIAL: INTERSECTS
OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS
OK PARALLEL VERTICAL: DO NOT INTERSECT
OK PARALLEL HORIZONTAL: DO NOT INTERSECT
OK PARALLEL SLOPE=.75: DO NOT INTERSECT
OK SEPERATE SEGMENTS: DO NOT INTERSECT
OK SEPERATE SEGMENTS 2: DO NOT INTERSECT
Я думал, что внес бы приятное решение Swift:
struct Pt {
var x: Double
var y: Double
}
struct LineSegment {
var p1: Pt
var p2: Pt
}
func doLineSegmentsIntersect(ls1: LineSegment, ls2: LineSegment) -> Bool {
if (ls1.p2.x-ls1.p1.x == 0) { //handle vertical segment1
if (ls2.p2.x-ls2.p1.x == 0) {
//both lines are vertical and parallel
return false
}
let x = ls1.p1.x
let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x)
let c2 = ls2.p1.y-slope2*ls2.p1.x
let y = x*slope2+c2 // y intersection point
return (y > ls1.p1.y && x < ls1.p2.y) || (y > ls1.p2.y && y < ls1.p1.y) // check if y is between y1,y2 in segment1
}
if (ls2.p2.x-ls2.p1.x == 0) { //handle vertical segment2
let x = ls2.p1.x
let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x)
let c1 = ls1.p1.y-slope1*ls1.p1.x
let y = x*slope1+c1 // y intersection point
return (y > ls2.p1.y && x < ls2.p2.y) || (y > ls2.p2.y && y < ls2.p1.y) // validate that y is between y1,y2 in segment2
}
let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x)
let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x)
if (slope1 == slope2) { //segments are parallel
return false
}
let c1 = ls1.p1.y-slope1*ls1.p1.x
let c2 = ls2.p1.y-slope2*ls2.p1.x
let x = (c2-c1)/(slope1-slope2)
return (((x > ls1.p1.x && x < ls1.p2.x) || (x > ls1.p2.x && x < ls1.p1.x)) &&
((x > ls2.p1.x && x < ls2.p2.x) || (x > ls2.p2.x && x < ls2.p1.x)))
//validate that x is between x1,x2 in both segments
}
Это мой способ проверки пересечения линии и места пересечения. Позволяет использовать x1 до x4 и y1 до y4
Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}
Тогда нам нужно несколько векторов, чтобы представить их
dx1 = X2 - X1
dx2 = X4 - X4
dy1 = Y2 - Y1
dy2 = Y4 - Y3
Теперь мы смотрим на определитель
det = dx1 * dy2 - dx2 * dy1
Если определитель равен 0,0, то отрезки линии параллельны. Это может означать, что они перекрываются. Если они перекрываются только в конечных точках, то существует одно решение пересечения. В противном случае будут бесконечные решения. Что бесконечно много решений, что сказать, это ваша точка пересечения? Так что это интересный частный случай. Если вы заранее знаете, что линии не могут пересекаться, тогда вы можете просто проверить, есть ли det == 0.0
и если это так, просто скажите, что они не пересекаются и это будет сделано. В противном случае, давайте продолжим
dx3 = X3 - X1
dy3 = Y3 - Y1
det1 = dx1 * dy3 - dx3 * dy1
det2 = dx2 * dy3 - dx3 * dy2
Теперь, если все det, det1 и det2 равны нулю, то ваши линии коллинеарны и могут перекрываться. Если det равно нулю, но det1 или det2 нет, то они не коллинеарны, а параллельны, поэтому пересечения нет. Итак, что осталось сейчас, если det равно нулю, это одномерная задача вместо 2D. Нам нужно будет проверить один из двух способов, в зависимости от того, равен ли dx1 нулю или нет (поэтому мы можем избежать деления на ноль). Если dx1 равно нулю, просто сделайте ту же логику со значениями y, а не x ниже.
s = X3 / dx1
t = X4 / dx1
Это вычисляет два скейлера, так что если мы масштабируем вектор (dx1, dy1) на s, мы получаем точку (x3, y3), а на t мы получаем (x4, y4). Так что, если s или t находится между 0,0 и 1,0, то точка 3 или 4 лежит на нашей первой строке. Отрицательный означает, что точка находится за началом нашего вектора, в то время как> 1.0 означает, что она находится дальше конца нашего вектора. 0.0 означает, что это в (x1, y1), и 1.0 означает, что это в (x2, y2). Если s и t <0.0 или оба> 1.0, они не пересекаются. И это обрабатывает параллельные линии в особом случае.
Теперь, если det != 0.0
то
s = det1 / det
t = det2 / det
if (s < 0.0 || s > 1.0 || t < 0.0 || t > 1.0)
return false // no intersect
Это похоже на то, что мы делали выше. Теперь, если мы пройдем вышеупомянутый тест, наши отрезки пересекаются, и мы можем легко вычислить пересечение следующим образом:
Ix = X1 + t * dx1
Iy = Y1 + t * dy1
Если вы хотите глубже погрузиться в то, что делает математика, изучите правило Крамера.
Проверить, пересекаются ли отрезки, очень легко с библиотекой Shapely, используя метод intersects
:
from shapely.geometry import LineString
line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 0)])
print(line.intersects(other))
# True
line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 2)])
print(line.intersects(other))
# False
Решено, но почему бы и нет с python...:)
def islineintersect(line1, line2):
i1 = [min(line1[0][0], line1[1][0]), max(line1[0][0], line1[1][0])]
i2 = [min(line2[0][0], line2[1][0]), max(line2[0][0], line2[1][0])]
ia = [max(i1[0], i2[0]), min(i1[1], i2[1])]
if max(line1[0][0], line1[1][0]) < min(line2[0][0], line2[1][0]):
return False
m1 = (line1[1][1] - line1[0][1]) * 1. / (line1[1][0] - line1[0][0]) * 1.
m2 = (line2[1][1] - line2[0][1]) * 1. / (line2[1][0] - line2[0][0]) * 1.
if m1 == m2:
return False
b1 = line1[0][1] - m1 * line1[0][0]
b2 = line2[0][1] - m2 * line2[0][0]
x1 = (b2 - b1) / (m1 - m2)
if (x1 < max(i1[0], i2[0])) or (x1 > min(i1[1], i2[1])):
return False
return True
Это:
print islineintersect([(15, 20), (100, 200)], [(210, 5), (23, 119)])
Вывод:
True
И это:
print islineintersect([(15, 20), (100, 200)], [(-1, -5), (-5, -5)])
Вывод:
False