与えられた2セットの点
((x1、y1、z1)、(x2、y2、z2)、(x3、y3、z3))および
((p1、q1、r1)、(p2、q2、r2)、(p3、q3、r3)))それぞれ3D空間で三角形を形成します。
これらの三角形が交差するかどうかはどうやってわかりますか?
この問題の明白な解決策の1つは、各三角形によって形成される平面の方程式を見つけることです。平面が平行である場合、それらは交差しません。
または、これらの平面の法線ベクトルを使用して、これらの平面の交差によって形成される線の方程式を見つけます。
ここで、この線が両方の三角形の領域にある場合、これらの2つの三角形は交差し、そうでない場合は交差しません。
trianglesIntersect(Triangle T1, Triangle T2)
{
if(trianglesOnParallelPlanes(T1, T2))
{
return false
}
Line L1 = lineFromPlanes(planeFromTriangle(T1), planeFromTriangle(T2))
if(lineOnTriangle(T1, L1) AND lineOnTriangle(T2, L1))
{
return true
}
return false
}
上記の関数の書き方を知っているとすれば、trianglesIntersectの他のどのような実装を検討すればよいですか?
この問題を解決するより高速なアルゴリズムはありますか?
幾何学的交差アルゴリズムのこのテーブルにアクセスしてくださいrealtimerendering.com の厚意により、三角形/三角形の交差のエントリを見て、以下に従ってくださいたとえば、Christer Ericsonへの参照、 リアルタイム衝突検出 、172ページ。(本私が強くお勧めします。)
基本的な考え方は簡単です。 2つの三角形が交差する場合、1つの三角形の2つのエッジが他の三角形と交差するか(下図の左側の構成)、または各三角形の1つのエッジが他の三角形と交差する(右側の構成)。
したがって、6つの線分と三角形の交差テストを実行し、これらの構成のいずれかが見つかるかどうかを確認します。
さて、あなたは尋ねます、線分/三角形の交差テストをどのように行うのですか?まあ、それは簡単です。 この幾何学的交差アルゴリズムの表 にアクセスし、線分(光線)/三角形交差のエントリを確認し、参照に従ってください...
(上で概説した簡単なテストはコプレーナ三角形を正しく処理しないことを言及することが重要です。多くのアプリケーションではこれは問題ではありません。たとえば、三角形のメッシュ間の衝突を検出するとき、コプレーナのケースはあいまいなので、どちらでもかまいません。結果が返されます。ただし、アプリケーションが例外の1つである場合は、特殊なケースとしてそれを確認するか、 分離など、他のいくつかの方法についてEricsonで読む必要があります軸法 、またはTomasMöllerの 間隔オーバーラップ法 )
方向付き境界ボックスを使用した動的衝突検出非共面三角形のみで説明されているアルゴリズムを実装しました。これは分離軸の定理に基づいています。
コード(collision-tests.js
内)とデモ/テストページを含むGitHubリポジトリは次のとおりです。
コードは、「ダイナミックコリジョン」ドキュメントの関連部分と一致するように意図的に記述されています。以下は、交差テスト関数(およびcollision-tests.js
ファイルの内容全体)です。 three.js三角形オブジェクトには、三角形の3つの頂点のa
、b
、およびc
プロパティがあることに注意してください。
/**
* @function
* @param {THREE.Triangle} t1 - Triangular face
* @param {THREE.Triangle} t2 - Triangular face
* @returns {boolean} Whether the two triangles intersect
*/
function doTrianglesIntersect(t1, t2) {
/*
Adapated from section "4.1 Separation of Triangles" of:
- [Dynamic Collision Detection using Oriented Bounding Boxes](https://www.geometrictools.com/Documentation/DynamicCollisionDetection.pdf)
*/
// Triangle 1:
var A0 = t1.a;
var A1 = t1.b;
var A2 = t1.c;
var E0 = A1.clone().sub(A0);
var E1 = A2.clone().sub(A0);
var E2 = E1.clone().sub(E0);
var N = E0.clone().cross(E1);
// Triangle 2:
var B0 = t2.a;
var B1 = t2.b;
var B2 = t2.c;
var F0 = B1.clone().sub(B0);
var F1 = B2.clone().sub(B0);
var F2 = F1.clone().sub(F0);
var M = F0.clone().cross(F1);
var D = B0.clone().sub(A0);
function areProjectionsSeparated(p0, p1, p2, q0, q1, q2) {
var min_p = Math.min(p0, p1, p2),
max_p = Math.max(p0, p1, p2),
min_q = Math.min(q0, q1, q2),
max_q = Math.max(q0, q1, q2);
return ((min_p > max_q) || (max_p < min_q));
}
// Only potential separating axes for non-parallel and non-coplanar triangles are tested.
// Seperating axis: N
{
var p0 = 0,
p1 = 0,
p2 = 0,
q0 = N.dot(D),
q1 = q0 + N.dot(F0),
q2 = q0 + N.dot(F1);
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Separating axis: M
{
var p0 = 0,
p1 = M.dot(E0),
p2 = M.dot(E1),
q0 = M.dot(D),
q1 = q0,
q2 = q0;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E0 × F0
{
var p0 = 0,
p1 = 0,
p2 = -(N.dot(F0)),
q0 = E0.clone().cross(F0).dot(D),
q1 = q0,
q2 = q0 + M.dot(E0);
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E0 × F1
{
var p0 = 0,
p1 = 0,
p2 = -(N.dot(F1)),
q0 = E0.clone().cross(F1).dot(D),
q1 = q0 - M.dot(E0),
q2 = q0;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E0 × F2
{
var p0 = 0,
p1 = 0,
p2 = -(N.dot(F2)),
q0 = E0.clone().cross(F2).dot(D),
q1 = q0 - M.dot(E0),
q2 = q1;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E1 × F0
{
var p0 = 0,
p1 = N.dot(F0),
p2 = 0,
q0 = E1.clone().cross(F0).dot(D),
q1 = q0,
q2 = q0 + M.dot(E1);
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E1 × F1
{
var p0 = 0,
p1 = N.dot(F1),
p2 = 0,
q0 = E1.clone().cross(F1).dot(D),
q1 = q0 - M.dot(E1),
q2 = q0;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E1 × F2
{
var p0 = 0,
p1 = N.dot(F2),
p2 = 0,
q0 = E1.clone().cross(F2).dot(D),
q1 = q0 - M.dot(E1),
q2 = q1;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E2 × F0
{
var p0 = 0,
p1 = N.dot(F0),
p2 = p1,
q0 = E2.clone().cross(F0).dot(D),
q1 = q0,
q2 = q0 + M.dot(E2);
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E2 × F1
{
var p0 = 0,
p1 = N.dot(F1),
p2 = p1,
q0 = E2.clone().cross(F1).dot(D),
q1 = q0 - M.dot(E2),
q2 = q0;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
// Seperating axis: E2 × F2
{
var p0 = 0,
p1 = N.dot(F2),
p2 = p1,
q0 = E2.clone().cross(F2).dot(D),
q1 = q0 - M.dot(E2),
q2 = q1;
if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
return false;
}
return true;
}