Попытка выяснить, пересекаются ли два отрезка
У меня есть отрезок, состоящий из двух точек p1 и p2, и второй отрезок, состоящий из точек p3 и p4. Я пытаюсь выяснить, пересекаются ли они, и пока мне не повезло. Это мой код до сих пор:
public static double angle(Point p1, Point p2, Point p3) {
double AB = length(p2, p1);
double BC = length(p2, p3);
double AC = length(p3, p1);
return Math.acos((sqr(BC) + sqr(AB) - sqr(AC)) / (2 * BC * AB)) * (180 / Math.PI);
}
public static boolean doIntersect(Point p1, Point p2, Point p3, Point p4) {
double a = angle(p4, p3, p2);
double b = angle(p3, p2, p1);
double c = 180 - b - a;
System.out.println("a: " + a + ", b: " + b + ", c:" + c);
if((length(p3, p2) * Math.sin(a)) / Math.sin(c) > length(p2, p1)) return false;
if((length(p3, p2) * Math.sin(b)) / Math.sin(c) > length(p3, p4)) return false;
return true;
}
public static double length(Point point1, Point point2) {
return Math.sqrt(sqr(point1.x - point2.x) + sqr(point1.y - point2.y));
}
public static double sqr(double doub) {
return Math.pow(doub, 2);
}
Но это не работает. Иногда угол "с" даже выражается как отрицательные числа.
Кроме того, Point - это пользовательский класс с двумя параметрами: x и y. Должно быть достаточно очевидным.
3 ответа
Для расчета углов вместо сложного метода, в котором вы суммируете квадраты разностей x и y, затем берете квадратный корень из суммы, затем возводите в квадрат это значение и затем используете acos
можно использовать atan2, который вычисляет угол в собственном квадранте из разностей x и y. Это быстрее и менее проблематично, чем acos
метод.
Тем не менее, нет никакой необходимости вычислять какие-либо углы вообще. Вместо этого сначала проверьте, если перекрытие невозможно. Возвращение No
если ((x₁≤x₃≤x₂ || x₁≤x₄≤x₂ || x₃≤x₁≤x₄ || x₃≤x₂≤x₄) && (y≤y₃≤y₂ || y₁≤y₄≤y₂ || y₃≤y₁≤y₄ || y₃≤y₂≤y₄))
ложно (Пусть p₁,p₂ - конечные точки одного сегмента, а p₃,p₄ - конечные точки другого.)
Если случай неосуществим, вы можете проверить, происходит ли пересечение, увидев, отличаются ли знаки площадей треугольников p₁,p₂,p₃ и p₁,p₂,p₄. Площадь p₁,p₂,p₃ равна ±(x₁·y₂-x₂·y₁ + x₃·(y₂-y₁) + y₃·(x₂-x₁))/2, и коэффициент 1/2 можно игнорировать, поскольку он не влияет на знак То есть для возможных случаев вы можете вычислитьt = x₁·y₂-x₂·y₁
u = t + x₃·(y₂-y₁) + y₃·(x₂-x₁)
v = t + x₄·(y₂-y₁) + y₄·(x₂-x₁)
и вернуться Yes
если u·v ≤ 0
иначе No
, Смысл использования такого метода состоит в том, чтобы избежать необходимости проверять все особые случаи, возникающие с вертикальными или горизонтальными линиями, параллельными или совпадающими линиями и т. Д. Если вы ожидаете появления вырожденных линий, сообщите Yes
если u·v < 0
, или же No
если u·v > 0
, иначе проверьте, если одна строка имеет конечную точку на другой линии.
Я бы предложил сначала сформулировать математическое решение, проверить его, а затем попытаться перенести его в компьютерную программу.
Есть два решения, каждое из которых имеет свои преимущества:
1. Математическое решение
Из двух точек p1 и p2 можно легко вывести формулу y = m*x + b
для линии, которая пересекает эти две точки. С помощью формул для линий, определенных (p1, p2) и (p3, p4), снова легко вычислить их пересечение путем выравнивания. Осталось только проверить, является ли точка пересечения частью обоих отрезков, что довольно очевидно.
2. Алгоритмическое решение
Другой подход заключается в применении алгоритма линии развертки: сортировка всех точек по их соответствующей x-координате. Затем для каждой точки в x-порядке представьте вертикальную линию, которая прыгает от точки к точке.
Если вы достигли первой точки отрезка линии B после того, как посетили все точки отрезка линии A, пересечения не будет.
В противном случае ваша первая точка находится на отрезке линии A, а вторая точка - на отрезке B. Следовательно, ваша третья и четвертая точки принадлежат A и B или B и A соответственно. Сосредоточьте свое внимание на координатах y и решите самую последнюю часть этого решения самостоятельно.;-)
РЕДАКТИРОВАТЬ: или еще проще, спросите Википедии о пересечении отрезка.