2つの平面間の交線を見つけるにはどうすればよいですか?
私は数学のアイデアを知っています、そして私は平面法線ベクトル間の外積をしました
しかし、プログラムで結果のベクトルから線を取得する方法
2つの平面間の交線を見つけるにはどうすればよいですか?
私は数学のアイデアを知っています、そして私は平面法線ベクトル間の外積をしました
しかし、プログラムで結果のベクトルから線を取得する方法
平面の方程式はですax + by + cz + d = 0
。ここで、(a、b、c)は平面の法線であり、dは原点までの距離です。これは、その方程式を満たすすべての点(x、y、z)が平面のメンバーであることを意味します。
与えられた2つの平面:
P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0
2つの交差点は、両方の方程式を検証する点のセットです。この線に沿った点を見つけるには、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
ただし、その線は原点を通過し、平面の交点に沿って走る線は通過しない場合があります。したがって、Martinhoの答えは、交線上の点(基本的には両方の平面上にある任意の点)を見つけるための素晴らしいスタートを提供します。
これを解決する方法の導出を見たい場合は、その背後にある数学を次に示します。
まず、x=0とします。これで、2つの方程式に3つの未知数があるのではなく、2つの方程式に2つの未知数があります(未知数の1つを任意に選択しました)。
次に、平面方程式は次のようになります(x = 0を選択したため、A項は削除されました)。
B 1 y + C 1 z + D 1 = 0
B 2 y + C 2 z + D 2 = 0
与えられたB1 、C 1に対して、これらの方程式が両方とも正しく解かれる(= 0)ようにyとzが必要です。
したがって、上の式に(-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
取得する式を追加します
z =((-B 2 / B 1)* D 1 -D 2)/(C 2 * B 2 / B 1)* C 1)
見つけたzを今すぐ最初の方程式に投げてyを次のように見つけます
y =(-D 1 -C 1 z)/ B 1
とにかく情報を持たないため、0を作成するのに最適な変数は係数が最も低い変数であることに注意してください。したがって、C1とC2が両方とも0の場合、(x = 0ではなく)z=0を選択する方が適切です。
上記の解決策は、B 1 = 0の場合でも失敗する可能性があります(これは可能性が低いことではありません)。B 1 = 0かどうかをチェックするifステートメントをいくつか追加できます。そうである場合は、代わりに他の変数の1つを必ず解決してください。
ユーザーの回答から、3つの平面の交差の閉じた形の解は実際にはGraphicsGems1にありました。式は次のとおりです。
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平面の交点です)
2つの平面に共通の交点ができたら、線はそのままになります。
P + t * d
ここで、Pは交点であり、tは(-inf、inf)から移動でき、dは2つの元の平面の法線の外積である方向ベクトルです。
赤と青の平面の交線はこんな感じ
「ロバスト」(2番目の方法)は、私のカウントでは48の基本操作を取りますが、1番目の方法(x、yの分離)が使用する36の基本操作を使用します。これら2つの方法の間には、安定性と#計算の間のトレードオフがあります。
B 1が0で、チェックしなかった場合、最初の方法への呼び出しから(0、inf、inf)を取り戻すのはかなり壊滅的です。したがって、if
ステートメントを追加し、最初の方法で0で除算しないようにすることで、コードの膨張と追加された分岐(かなり高価になる可能性があります)を犠牲にして安定性を得ることができます。3平面交差法はほとんど分岐がなく、無限大にはなりません。
完全を期すためにこの回答を追加します。これを書いている時点では、ここの回答には、質問に直接対処する実用的なコード例が含まれていません。
ここでの他の答えはすでに原則をカバーしていますが。
2つの平面間の線を見つけることは、3平面交差アルゴリズムの簡略化されたバージョンを使用して計算できます。
boboboboの回答からの2番目の「より堅牢な方法」は、3平面の交点を参照しています。
これは2つの平面(最初の2つの外積を使用して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;
}
}
この方法では、2つの平面が平行でない限り、ゼロによる除算を回避します。
これらが飛行機の場合:
A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0
1)交線に平行なベクトルを見つけます。これは、他の2つの平面に垂直な3番目の平面の法線でもあります。
(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
)の最も近い点に移動し、次にそこから2つの平面の交線上の最も近い点に移動することです。(私が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)
これは、行列式ベースのアプローチと同じポイントを与えるはずです。ほぼ確実に、2つの間にリンクがあります。n2 • v
「スカラー三重積」規則を適用した場合、少なくとも分母は同じです。したがって、これらの方法は、条件数に関する限り、おそらく類似しています。
(ほぼ)平行な平面をチェックすることを忘れないでください。例:if (dir • dir < 1e-8)
単位法線が使用されている場合はうまく機能するはずです。
線の外積は、交線の方向です。ここで、交差点にポイントが必要です。
これを行うには、外積上の点を取り、平面Aの法平面*平面Aまでの距離と平面Bの法平面*平面bまでの距離を差し引きます。クリーナー:
p=外積上のポイント
交点=([p]-([平面Aの法線] * [pから平面Aまでの距離])-([平面Bの法線] * [pから平面Bまでの距離]))
編集:
2つの法線を持つ2つの平面があります。
N1 and N2
外積は、交差線の方向です。
C = N1 x N2
上記のクラスには、点と平面の間の距離を計算する関数があります。これを使用して、C上のある点pから両方の平面までの距離を取得します。
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