迅速で汚れた方法の1つは、再帰的な球状サブディビジョンを使用します。地表の三角形分割から始めて、各三角形を頂点からその最長辺の中央まで再帰的に分割します。(理想的には、三角形を2つの等しい直径の部分または等しい面積の部分に分割しますが、それらは面倒な計算を伴うため、辺をちょうど半分に分割します。これにより、さまざまな三角形のサイズが最終的に少し異なりますが、これは、このアプリケーションにとって重要ではないと思われます。)
もちろん、このサブディビジョンをデータ構造で維持することで、任意の点が存在する三角形をすばやく識別できるようになります。バイナリツリー(再帰呼び出しに基づく)はうまく機能します。三角形が分割されるたびに、ツリーはその三角形のノードで分割されます。分割平面に関するデータが保持されるため、任意の点が平面のどちら側にあるかをすばやく判断できます。これにより、ツリーを左または右に移動するかどうかが決まります。
(「平面」を分割すると言いましたか?はい-地球の表面を球体としてモデル化し、地理中心(x、y、z)座標を使用する場合、ほとんどの計算は3次元で行われ、三角形の辺は球とその原点を通る平面の交差。これにより、計算が迅速かつ簡単になります。)
球の1つのオクタントでの手順を示して説明します。他の7つのオクタントは同じ方法で処理されます。そのような八分円は、90-90-90の三角形です。私のグラフィックスでは、同じコーナーにまたがるユークリッド三角形を描画します。それらは、小さくなるまで見栄えがよくありませんが、簡単かつ迅速に描画できます。八分円に対応するユークリッド三角形は次のとおりです。これが手順の始まりです。
すべての辺の長さが等しいため、「最長」としてランダムに選択され、細分化されます。
新しい三角形のそれぞれについて、これを繰り返します。
nステップ後、2 ^ n個の三角形ができます。これは、10ステップ後の状況で、オクタントに1024個の三角形(および球全体に8192個)を示しています。
もう1つの例として、このオクタント内にランダムなポイントを生成し、三角形の最も長い辺が0.05ラジアン未満になるまでサブディビジョンツリーを移動しました。(デカルト)三角形は、プローブポイントが赤で表示されます。
ちなみに、ポイントの位置を1度の緯度(およそ)に狭めると、これは約1/60ラジアンであり、約(1/60)^ 2 /(Pi / 2)= 1/6000をカバーすることに注意してください。全表面。各サブディビジョンは三角形のサイズを約半分にするので、オクタントの約13〜14サブディビジョンでうまくいきます。以下で説明するように、これはあまり計算ではありません。ツリーをまったく保存せずに、その場でサブディビジョンを実行するのが効率的です。最初に、ポイントがどのオクタントにあるかを確認します。これは、3桁の2進数として記録できる3つの座標の符号によって決定されます。各ステップで、ポイントが存在するかどうかを覚えておきます。三角形の左側(0)または右側(1)。これにより、別の14桁の2進数が得られます。 これらのコードを使用して、任意のポイントをグループ化できます。
(一般的に、2つのコードが実際の2進数に近い場合、対応するポイントは近くなりますが、ポイントは依然として近く、コードが著しく異なる場合があります。たとえば、赤道から1メートル離れた2つのポイントを考えます。たとえば、コードは異なる必要がありますバイナリポイントの前。これらは異なるオクタントにあるため。この種のことは、スペースの固定パーティションで避けられません。)
私はMathematica 8を使用してこれを実装しました:それをそのまま、またはお気に入りのプログラミング環境での実装の疑似コードとして使用できます。
平面0-abポイントpが存在する側を決定します。
side[p_, {a_, b_}] := If[Det[{p, a, b}] >= 0, left, right];
点pに基づいて三角形abcを調整します。
refine[p_, {a_, b_, c_}] := Block[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
If[side[p, {x, m}] === right, {y, m, x}, {x, m, z}]
]
最後の図は、オクタントを表示することによって、さらにその上に、次のリストをポリゴンのセットとしてレンダリングすることによって描かれました。
p = Normalize@RandomReal[NormalDistribution[0, 1], 3] (* Random point *)
{a, b, c} = IdentityMatrix[3] . DiagonalMatrix[Sign[p]] // N (* First octant *)
NestWhileList[refine[p, #] &, {a, b, c}, Norm[#[[1]] - #[[2]]] >= 0.05 &, 1, 16]
NestWhileList
refine
条件が関係している間(三角形が大きい場合)、または最大操作カウントに達するまで(16)、操作を繰り返し適用します()。
オクタントの完全な三角測量を表示するために、最初のオクタントから始めて、精製を10回繰り返しました。これは、次のわずかな変更から始まりますrefine
。
split[{a_, b_, c_}] := Module[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
{{y, m, x}, {x, m, z}}
]
違いは、指定された点がある三角形ではなく、入力三角形の両方の半分をsplit
返すことです。完全な三角形分割は、これを繰り返すことによって得られます。
triangles = NestList[Flatten[split /@ #, 1] &, {IdentityMatrix[3] // N}, 10];
確認するために、すべての三角形のサイズの測定値を計算し、範囲を調べました。(この「サイズ」は、各三角形と球体の中心によって範囲が定められたピラミッド型の図形に比例します。これらのような小さな三角形の場合、このサイズは基本的にその球形領域に比例します。)
Through[{Min, Max}[Map[Round[Det[#], 0.00001] &, triangles[[10]] // N, {1}]]]
{0.00523、0.00739}
したがって、サイズは平均から約25%上下します。これは、ポイントをグループ化するためのほぼ均一な方法を達成するために妥当なようです。
このコードをスキャンすると、三角法に気付かないでしょう。球形座標とデカルト座標の間で前後に変換する場合に必要な唯一の場所です。また、コードは地球の表面をマップに投影しないため、付随する歪みを回避できます。それ以外の場合は、平均化(Mean
)、ピタゴラスの定理(Norm
)、3 x 3の行列式(Det
)のみを使用してすべての作業を行います。(RotateLeft
およびのようないくつかの単純なリスト操作コマンドとFlatten
、各三角形の最も長い辺の検索があります。)