Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 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