球充填による3次元空間上への球体の配置

球を隙間なく平面と3次元空間上に配置する方法についてです。プログラミング言語で任意の大きさの球充填を生成する方法について説明します。


平面の場合の球充填

球充填は空間上に球を隙間なく配置することを言います。平面上では以下のような球の配置が球充填と呼ばれます。
平面における球充填($P=3$)
ここで球の半径を$r$と置いています。この図の場合、外側の球の中心を結ぶと一辺が$4r$の正三角形ができます。球充填の大きさ$P$を正三角形の一片の上に並んでいる球の数と定義しておきます。この図の場合$P = 3$です。

一般化して$P$が任意の値の場合を考えると、外側の球の中心を結んで作られる正三角形の一辺の長さは$2(P - 1)r$、正三角形の高さは$(P - 1)\sqrt{3}r$になります。

正三角形に注目して球の位置を計算することができます。左下の球が座標$(0, 0)$にあるとします。すると右隣の球の位置は$(2r, 0)$、すぐ右上の球の位置は$(r, \sqrt{3}r)$になります。
球の位置
右に移動するときは$(2r, 0)$、上に移動するときは$(r, \sqrt{3}r)$だけ座標をずらしていけばよいことが分かります。これはプログラムで球充填を生成するときに使えます。

また、正三角形の中心は$((P - 1)r, \frac{(P - 1)\sqrt{3}}{2}r)$で与えられます。これもプログラムで描画するときに画面の中心に持ってくるようにするために使えます。

プログラムで球充填を生成するにはfor文で二重ループを作り、それぞれのループで球の位置を右と上にずらしていけばよいことになります。Processingを用いると次のように書けます。
size(512, 512);


int P = 5;
float r = 20;

float X = (P - 1)*r;
float Y = (P - 1)*sqrt(3)*r/2;

for(int i = 0; i < P; i++) {
  for(int j = 0; j < P; j++) {
    ellipse(width/2 - X + 2*r*j + r*i, height/2 - Y  + sqrt(3)*r*i, 2*r, 2*r);
  }
}

これを実行すると次のような画像を得ます。
実行結果
形をきれいな三角形状にする場合、for文の範囲とインデックスの取り方を調整して次のようにすればよいです。

size(512, 512);


int P = 5;
float r = 20;

float X = (P - 1)*r;
float Y = (P - 1)*sqrt(3)*r/2;

for(int i = 0; i < P; i++) {
  for(int j = i; j < P; j++) {
    float x = j - i;
    float y = i;
    ellipse(width/2 - X + 2*r*x + r*y, height/2 - Y  + sqrt(3)*r*y, 2*r, 2*r);
  }
}
これを実行すると次のような画像を得ます。
実行結果
きれいな三角形状になりました。

3次元空間における球充填

平面の場合を拡張して、3次元空間における球充填を生成する方法について考えます。3次元空間上では球を隙間なく敷き詰める方法として幾つか考えられるのですが、ここではピラミッド状に球が敷き詰められている場合を考えます。
3次元空間における球充填

横向きをX軸、奥行きをZ軸、上向きをY軸と定義します。するとXZ平面に関しては平面の球充填の場合と同様のことが成り立ちます。新たに考える必要があるのはY軸方向に関してです。

一つ上にある球は、下にある3つの球で構成されるくぼみの上に乗っかる形になります。
くぼみの位置

くぼみのXZ平面における位置は、下にある3つの球で構成される正三角形の重心にあります。したがってXZ平面における上に乗っかる球の位置は$(r, \frac{\sqrt{3}}{3}r)$と求まります。

あとはY軸についてです。下にある3つの球と、上に乗っかる球の中心を結んでいくと、正四面体を作ることができます。
4つの球から作られる正四面体
すると上にある球のY軸方向の位置は正四面体の高さで決まることが分かります。一辺が$a$の正四面体の高さは$\frac{\sqrt{6}}{3}a$で与えられるので、$a = 2r$として、上の球のY軸方向の位置は$\frac{2\sqrt{6}}{3}r$と求まります。

まとめると球が一つ上にずれると$(r, \frac{2\sqrt{6}}{3}r, \frac{\sqrt{3}}{3}r)$だけ位置がずれます。

また、大きさ$P$の球充填全体で作られる正四面体の中心位置は$((P - 1)r, (P - 1)\frac{\sqrt{6}}{3}r, \frac{(P - 1)\sqrt{3}}{2}r)$で与えられます。

以上のことを使って、Processingを用いて3次元空間上の球充填を生成すると次のようになります。

size(512, 512, P3D);
background(0);
lights();

int P = 5;
float r = 20;

float X = (P - 1)*r;
float Y = (P - 1)*sqrt(6)/3*r;
float Z = (P - 1)*sqrt(3)*r/2;

for(int i = 0; i < P; i++) {
  for(int j = i; j < P; j++) {
    for(int k = j; k < P; k++) {
      float x = k - j;
      float y = i;
      float z = j - i;
      pushMatrix();
      translate(width/2 - X + 2*r*x + r*z + r*y, height/2 - Y + sqrt(6)/3*2*r*y, -Z + sqrt(3)*r*z + sqrt(3)/3*r*y);
      noStroke();
      sphere(r);
      popMatrix();
    }
  }
}
これを実行すると次のような画像を得ます。
生成された画像
ピラミッド型に球が充填されているのが分かります。

3次元空間における球充填の例としてお見せした画像は、自前のレイトレーサーでProcessingの例と同様に球を配置してレンダリングすることで得たものです。見た目が面白いので色々な場所で使えると思います。

Comments