Как найти линию пересечения между двумя плоскостями?
Я знаю идею математики, и я сделал крестовое произведение между нормальными векторами плоскости
но как получить линию из приведенного вектора программно
Как найти линию пересечения между двумя плоскостями?
Я знаю идею математики, и я сделал крестовое произведение между нормальными векторами плоскости
но как получить линию из приведенного вектора программно
Добавление этого ответа для полноты, поскольку на момент написания статьи ни один из ответов здесь не содержит рабочего кода-примера, который непосредственно решает вопрос.
Хотя другие ответы здесь уже охватывают принципы.
Поиск линии между двумя плоскостями может быть рассчитан с использованием упрощенной версии алгоритма пересечения трех плоскостей.
2-й, "более надежный метод" из ответа bobobobo ссылается на 3-плоскостное пересечение.
Хотя это хорошо работает для двух плоскостей (где 3-я плоскость может быть рассчитана с использованием поперечного произведения первых двух), проблема может быть дополнительно уменьшена для версии с 2 плоскостями.
Включая этот пример кода, так как он может быть не сразу очевидным.
// 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;
}
}
Уравнение плоскости 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)
Чтобы получить пересечение двух плоскостей, вам нужна точка на линии и направление этой линии.
Найти направление этой линии очень просто, просто пересечь 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)
Как только у вас есть точка пересечения, общая для двух плоскостей, линия просто идет
P + t * d
Где P - точка пересечения, t может идти от (-inf, inf), а d - вектор направления, являющийся поперечным произведением нормалей двух исходных плоскостей.
Линия пересечения между красной и синей плоскостями выглядит так:
"Надежный" (2-й способ) принимает 48 элементарных операций по моему счету, по сравнению с 36 элементарными операциями, которые использует 1-й способ (выделение x, y). Существует обмен между стабильностью и # вычислением между этими двумя способами.
Было бы довольно катастрофично получить (0, inf, inf) обратно от вызова к 1-му пути в случае, если B 1 равен 0, и вы не проверяли. Поэтому добавление в операторы if
и не разделить на 0 на 1-й способ может дать вам стабильность за счет разбухания кода и добавленное ветвление (что может быть довольно дорого). Метод 3-х плоскостных пересечений почти бесконтактный и не даст вам бесконечности.
Этот метод позволяет избежать деления на ноль, если две плоскости не параллельны.
Если это плоскости:
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
Подход, основанный на определении, является опрятным, но трудно понять, почему он работает.
Вот еще один способ, который более интуитивно понятен.
Идея состоит в том, чтобы сначала перейти от начала координат к ближайшей точке на первой плоскости (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)
должен хорошо работать, если используются нормальные единицы.
Перекрестное произведение линии - это направление линии пересечения. Теперь вам нужна точка в пересечении.
Вы можете сделать это, взяв точку на перекрестном произведении, затем вычитая нормаль плоскости 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