Линия пересечения двух плоскостей
Как найти линию пересечения между двумя плоскостями?
Я знаю идею математики, и я сделал крестовое произведение между нормальными векторами плоскости
но как получить линию из приведенного вектора программно
Ответы
Ответ 1
Добавление этого ответа для полноты, поскольку на момент написания статьи ни один из ответов здесь не содержит рабочего кода-примера, который непосредственно решает вопрос.
Хотя другие ответы здесь уже охватывают принципы.
Поиск линии между двумя плоскостями может быть рассчитан с использованием упрощенной версии алгоритма пересечения трех плоскостей.
2-й, "более надежный метод" из ответа bobobobo ссылается на 3-плоскостное пересечение.
Хотя это хорошо работает для двух плоскостей (где 3-я плоскость может быть рассчитана с использованием поперечного произведения первых двух), проблема может быть дополнительно уменьшена для версии с 2 плоскостями.
- Не нужно использовать матричный определитель 3x3, вместо этого мы можем использовать квадрат длины поперечного произведения между первой и второй плоскостью (которая является направлением 3-й плоскости).
- Не нужно включать расстояние 3-й плоскости,
(вычисление конечного местоположения).
- Не нужно отменять расстояния.
Сохраните некоторые циклы процессора, заменив вместо этого заказ на перекрестный продукт.
Включая этот пример кода, так как он может быть не сразу очевидным.
// Intersection of 2-planes: a variation based on the 3-plane version.
// see: Graphics Gems 1 pg 305
//
// Note that the 'normal' components of the planes need not be unit length
bool isect_plane_plane_to_normal_ray(
const Plane& p1, const Plane& p2,
// output args
Vector3f& r_point, Vector3f& r_normal)
{
// logically the 3rd plane, but we only use the normal component.
const Vector3f p3_normal = p1.normal.cross(p2.normal);
const float det = p3_normal.length_squared();
// If the determinant is 0, that means parallel planes, no intersection.
// note: you may want to check against an epsilon value here.
if (det != 0.0) {
// calculate the final (point, normal)
r_point = ((p3_normal.cross(p2.normal) * p1.d) +
(p1.normal.cross(p3_normal) * p2.d)) / det;
r_normal = p3_normal;
return true;
}
else {
return false;
}
}
Ответ 2
Уравнение плоскости ax + by + cz + d = 0
, где (a, b, c) - плоская нормаль, d - расстояние до начала координат. Это означает, что каждая точка (x, y, z), которая удовлетворяет этому уравнению, является членом плоскости.
Учитывая две плоскости:
P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0
Пересечение между ними - это множество точек, которые проверяют оба уравнения. Чтобы найти точки вдоль этой линии, вы можете просто выбрать значение для x, любое значение, а затем решить уравнения для y и z.
y = (-c1z -a1x -d1) / b1
z = ((b2/b1)*(a1x+d1) -a2x -d2)/(c2 - c1*b2/b1)
Если вы делаете x=0
, это становится проще:
y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)
Ответ 3
Поиск точки на линии
Чтобы получить пересечение двух плоскостей, вам нужна точка на линии и направление этой линии.
Найти направление этой линии очень просто, просто пересечь 2 нормали двух плоскостей, которые пересекаются.
lineDir = n1 × n2
Но эта строка проходит через начало координат, и линия, которая проходит вдоль ваших пересечений плоскости, может и не быть. Итак, ответ Martinho дает отличное начало для поиска точки на линии пересечения (в основном любой точки, которая находится на обеих плоскостях).
Если вы хотите увидеть вывод о том, как решить эту проблему, вот математика за ней:
Сначала пусть x = 0. Теперь у нас есть 2 неизвестных в 2 уравнениях вместо 3 неизвестных в 2 уравнениях (мы произвольно выбрали одно из неизвестных).
Тогда плоские уравнения (члены A исключены, так как мы выбрали x = 0):
B 1 y + C 1 z + D 1= 0
B 2 y + C 2 z + D 2= 0
Мы хотим, чтобы у и z такие, что эти уравнения решались правильно (= 0) для B 1, C 1.
Итак, просто умножьте верхний eq на (-B 2/B 1), чтобы получить
-B 2 y + (-B 2/B 1) * C 1 z + ( -B 2/B 1) * D 1= 0
B 2 y + C 2 z + D 2= 0
Добавьте eqs для получения
z = ((-B 2/B 1) * D 1 - D 2)/(C 2 * B 2/B 1) * C 1)
Бросьте z, который вы найдете в 1-ом уравнении теперь, чтобы найти y как
y = (-D 1 - C 1 z)/B 1
Обратите внимание, что самая лучшая переменная, которая делает 0, является той, которая имеет самые низкие коэффициенты, поскольку она не имеет никакой информации. Поэтому, если C 1 и C 2 были равны 0, выбор z = 0 (вместо x = 0) был бы лучшим выбором.
Вышеупомянутое решение все еще может быть испорчено, если B 1= 0 (что не так маловероятно). Вы можете добавить в некоторые операторы if, которые проверяют, если B 1= 0, и если это так, не забудьте решить одну из других переменных.
Решение с использованием пересечения трех плоскостей
В ответе пользователя решение закрытой формы для пересечения трех плоскостей было фактически в Графических камнях 1. Формула:
P_intersection = ((point_on1 • n1) (n2 × n3) + (point_on2 • n2) (n3 × n1) + (point_on3 • n3) (n1 × n2))/det (n1, n2, n3)
Фактически point_on1 • n1 = -d1 (при условии, что вы пишете ваши плоскости Ax + By + Cz + D = 0, а не = -D). Итак, вы можете переписать его как:
P_intersection = ((-d1) (n2 × n3) + (-d2) (n3 × n1) + (-d3) (n1 × n2))/det (n1, n2, n3)
Функция, пересекающая 3 плоскости:
// Intersection of 3 planes, Graphics Gems 1 pg 305
static Vector3f getIntersection( const Plane& plane1, const Plane& plane2, const Plane& plane3 )
{
float det = Matrix3f::det( plane1.normal, plane2.normal, plane3.normal ) ;
// If the determinant is 0, that means parallel planes, no intn.
if( det == 0.f ) return 0 ; //could return inf or whatever
return ( plane2.normal.cross( plane3.normal )*-plane1.d +
plane3.normal.cross( plane1.normal )*-plane2.d +
plane1.normal.cross( plane2.normal )*-plane3.d ) / det ;
}
Доказательство его работы (желтая точка - это пересечение плоскостей rgb)
![enter image description here]()
Получение строки
Как только у вас есть точка пересечения, общая для двух плоскостей, линия просто идет
P + t * d
Где P - точка пересечения, t может идти от (-inf, inf), а d - вектор направления, являющийся поперечным произведением нормалей двух исходных плоскостей.
Линия пересечения между красной и синей плоскостями выглядит так:
![enter image description here]()
Эффективность и стабильность
"Надежный" (2-й способ) принимает 48 элементарных операций по моему счету, по сравнению с 36 элементарными операциями, которые использует 1-й способ (выделение x, y). Существует обмен между стабильностью и # вычислением между этими двумя способами.
Было бы довольно катастрофично получить (0, inf, inf) обратно от вызова к 1-му пути в случае, если B 1 равен 0, и вы не проверяли. Поэтому добавление в операторы if
и не разделить на 0 на 1-й способ может дать вам стабильность за счет разбухания кода и добавленное ветвление (что может быть довольно дорого). Метод 3-х плоскостных пересечений почти бесконтактный и не даст вам бесконечности.
Ответ 4
Этот метод позволяет избежать деления на ноль, если две плоскости не параллельны.
Если это плоскости:
A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0
1) Найдите вектор, параллельный линии пересечения. Это также нормаль третьей плоскости, перпендикулярной двум другим плоскостям:
(A3,B3,C3) = (A1,B1,C1) cross (A2,B2,C2)
2) Составьте систему из трех уравнений. Они описывают 3 плоскости, которые пересекаются в точке:
A1*x1 + B1*y1 + C1*z1 + D1 = 0
A2*x1 + B2*y1 + C2*z1 + D2 = 0
A3*x1 + B3*y1 + C3*z1 = 0
3) Решите их, чтобы найти x1, y1, z1. Это точка на линии пересечения.
4) Параметрическими уравнениями линии пересечения являются:
x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t
Ответ 5
Подход, основанный на определении, является опрятным, но трудно понять, почему он работает.
Вот еще один способ, который более интуитивно понятен.
Идея состоит в том, чтобы сначала перейти от начала координат к ближайшей точке на первой плоскости (p1
), а затем оттуда перейти к ближайшей точке на линии пересечения двух плоскостей. (Вдоль вектора, который я называю v
ниже.)
Given
=====
First plane: n1 • r = k1
Second plane: n2 • r = k2
Working
=======
dir = n1 × n2
p1 = (k1 / (n1 • n1)) * n1
v = n1 × dir
pt = LineIntersectPlane(line = (p1, v), plane = (n2, k2))
LineIntersectPlane
==================
#We have n2 • (p1 + lambda * v) = k2
lambda = (k2 - n2 • p1) / (n2 • v)
Return p1 + lambda * v
Output
======
Line where two planes intersect: (pt, dir)
Это должно дать ту же точку, что и подход, основанный на определении. Там почти наверняка есть связь между ними. По крайней мере, знаменатель n2 • v
, тот же, если применить правило "скалярного тройного произведения". Таким образом, эти методы, вероятно, схожи, поскольку номера условий идут.
Не забудьте проверить (почти) параллельные плоскости. Например: if (dir • dir < 1e-8)
должен хорошо работать, если используются нормальные единицы.
Ответ 6
Перекрестное произведение линии - это направление линии пересечения. Теперь вам нужна точка в пересечении.
Вы можете сделать это, взяв точку на перекрестном произведении, затем вычитая нормаль плоскости A * расстояния до плоскости A и нормали плоскости B * до плоскости b. Очиститель:
p = Точка на поперечном произведении
точка пересечения = ([p] - ([Нормальная плоскость A] * [расстояние от p до плоскости A]) - ([Нормальная плоскость B] * [расстояние от p до плоскости B]))
Edit:
У вас есть две плоскости с двумя нормалями:
N1 and N2
Перекрестное произведение - это направление линии пересечения:
C = N1 x N2
В приведенном выше классе есть функция для вычисления расстояния между точкой и плоскостью. Используйте его, чтобы получить расстояние до некоторой точки p на C до обеих плоскостей:
p = C //p = 1 times C to get a point on C
d1 = plane1.getDistance(p)
d2 = plane2.getDistance(p)
Линия пересечения:
resultPoint1 = (p - (d1 * N1) - (d2 * N2))
resultPoint2 = resultPoint1 + C