Линия пересечения двух плоскостей

Как найти линию пересечения двух плоскостей?

Я знаю математическую идею, и я сделал перекрестное произведение между плоскостями нормальных векторов

но как получить строку из результирующего вектора программно

6 ответов


добавление этого ответа для полноты, так как на момент написания ни один из ответов здесь не содержит рабочего примера кода, который непосредственно затрагивает вопрос.

хотя другие ответы здесь уже охвачены принципы.


Поиск линии между двумя плоскостями можно вычислить, используя упрощенную версию алгоритма пересечения 3-плоскостей.

2'nd ," более надежный метод " от bobobobo это ответ ссылается на пересечение 3-плоскости.

в то время как это хорошо работает для 2 плоскостей (где 3-я плоскость может быть рассчитана с использованием поперечного произведения первых двух), проблема может быть дополнительно уменьшена для версии 2-plane.

  • нет необходимости использовать определитель матрицы 3x3,
    вместо этого мы можем использовать квадратную длину поперечного произведения между первой и второй плоскостью (это направление 3 Самолет-ей).
  • нет необходимости включать расстояние 3-й плоскости,
    (вычисление конечного местоположения).
  • нет необходимости отрицать расстояния.
    сохраните некоторые cpu-циклы, заменив вместо этого кросс - заказ продукта.

включая этот пример кода, так как это может быть не сразу очевидно.

// 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 плоскостей, вам нужна точка на линии и направление этой линии.

найти направление этой линии очень легко, просто пересечь 2 нормали 2 плоскостей, которые пересекаются.

lineDir = n1 × n2

но эта линия проходит через начало координат, а линия, которая проходит вдоль ваших плоских пересечений, может и нет. Итак,Мартинью-х ответ начать поиск точка на линии пересечения (в основном любой точка, которая находится на обеих плоскостях).

если вы хотите увидеть вывод для того, как это решить, вот математика за ним:

сначала пусть x=0. Теперь у нас есть 2 неизвестных в 2 уравнениях вместо 3 неизвестных в 2 уравнениях (мы произвольно выбрали одно из неизвестных).

тогда уравнения плоскости (члены были исключены, так как мы выбрали x=0):

B1y + C1z + D1 = 0

B2y + C2z + D2 = 0

мы хотим, чтобы y и z такие, что эти уравнения оба решаются правильно (=0) для B1, C1 дали.

Итак, просто умножьте верхний эквалайзер на (- B2 / B1) для получения

- B2y + (- B2 / B1) * C1z + (- B2 / B1) * D1 = 0

B2y + C2z + D2 = 0

добавить эквалайзер, чтобы получить

z = ((- B2 / B1) * D1 - D2 ) / (C2 * Б2 / B1) * C1)

бросьте z, который вы найдете в 1-м уравнении, чтобы найти y как

y = (- D1 - C1z) / B1

Примечание лучшие переменной, чтобы сделать 0-с низкий коэффициенты, потому что он не несет никакой информации в любом случае. Так что если C1 и C2 были оба 0, выбирая z=0 (вместо x=0), были бы лучшим выбором.

вышеуказанное решение все еще может испортить, если B1=0 (что не так уж маловероятно). Вы можете добавить некоторые операторы if, которые проверяют, если b1=0, и если это так,обязательно решите для одной из других переменных.

решение с использованием пересечения 3 плоскостей

С пользователя ответ, решение замкнутой формы для пересечения 3 плоскостей было собственно в графике Gems 1. Формула такова:

P_intersection = (( point_on1 • П1 )( П2 × Н3 ) + ( point_on2 • Н2), (Н3 × Н1 ) + ( point_on3 • П3 )( П1 × П2 )) / дет(Н1,Н2,Н3)

фактически 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

начало строки

как только у вас есть точка пересечения, общая для 2 плоскостей, линия просто идет

P + t * d

где P-точка пересечения, t может идти от (- inf, inf), А d-вектор направления, который перекрестное произведение нормалей двух исходных плоскостей.

линия пересечения между красной и синей плоскостями выглядит так

enter image description here

эффективность и стабильность

"надежный" (2-й способ) занимает 48 элементарных операций по моему счету, против 36 элементарных операций,которые использует 1-й способ (изоляция x, y). Существует компромисс между стабильностью и # вычислениями между этими 2 способами.

Это было бы довольно катастрофично, чтобы получить (0,inf,inf) назад от вызова к 1-му пути в случае, если B1 было 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 уравнений. Они описывают 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