LINQ をリスト内包記法として使う例

リストの内包表記(list comprehensions) - ++C++; // 管理人の日記で書いたリスト内包の利用例として、ふと思いついたことをやってみる。
3次元上の格子点を平面に投影するっての。
3次元の格子点って言うと、
L = \{(x, y, z) \in \bold{R}^3 | x \in \bold{N}, y \in \bold{N}, z \in \bold{N} \}
で、とある平面への投影関数を Pr とでも置いて、
P = \{Pr(p) \in \bold{R}^2 | p \in L\}
がどういう点を描くかという話。↑のPは、例えば、投影したい平面の正規直交基底ベクトルをi_1, i_2とすると、
P = \{(p\cdot i_1, p\cdot i_2) \in \bold{R}^2 | p \in L\}
と書けるような点の集合になる。

まず、3次元の格子点は C# 3.0 的に書くと、

var points3d =
	from x in Enumerable.Range(-M, 2 * M)
	from y in Enumerable.Range(-M, 2 * M)
	from z in Enumerable.Range(-M, 2 * M)
	select new Vector3D(x, y, z);

てな感じ。M は適当な整数の定数。探索範囲を絞るために。Vector3D は、WPF の System.Windows.Media.Media3D.Vector3D クラス。
これに対して、ある法線ベクトル normal と、それに直交する基底2つ i1, i2 を与えて、

var projectedPoints =
	from p in points3d
	let x = Vector3D.DotProduct(p, i1)
	let y = Vector3D.DotProduct(p, i2)
	select new Point(x, y);

で投影完了。結構、数学のイメージどおりのコードになってるんじゃないかと。
法線ベクトル normal から、適当な残り2つの直交基底を求めるには以下のような関数を書けばOK。

static void GetOrthogonalBase(Vector3D i1, out Vector3D i2, out Vector3D i3)
{
	Vector3D temp;

	if (i1.X > i1.Y)
		if (i1.X > i1.Z) temp = new Vector3D(0, 1, 0);
		else temp = new Vector3D(1, 0, 0);
	else
		if (i1.Y > i1.Z) temp = new Vector3D(0, 0, 1);
		else temp = new Vector3D(1, 0, 0);

	i2 = Vector3D.CrossProduct(i1, temp);
	i2.Normalize();

	i3 = Vector3D.CrossProduct(i1, i2);
	i3.Normalize();
}

探索範囲とかを絞って(指定した平面から遠い点は除外&結果として表示されない範囲も除外)効率化するために、↓みたいな条件も付加。

var points3d =
	from x in Enumerable.Range(-M, 2 * M)
	from y in Enumerable.Range(-M, 2 * M)
	from z in Enumerable.Range(-M, 2 * M)
	select new Vector3D(x, y, z) into p
	let distance = Math.Abs(Vector3D.DotProduct(p, normal))
	where distance < THICKNESS
	select p;

Vector3D i1, i2;
GetOrthogonalBase(normal, out i1, out i2);

var projectedPoints =
	from p in points3d
	let x = Vector3D.DotProduct(p, i1)
	let y = Vector3D.DotProduct(p, i2)
	where -WIDTH <= x && x <= WIDTH
		&& -WIDTH <= y && y <= WIDTH
	select new Point(x, y);

結果、例えば・・・
法線が (1, 0, 0) みたいなのだと、投影結果もきれいに格子点になって、↓みたいな感じ。
http://ufcpp.net/study/csharp/fig/projection/1_0_0.png
法線を (1, 1, 1) とかにすると、最密充填な感じの6角形状の点が得られる↓。
http://ufcpp.net/study/csharp/fig/projection/1_1_1.png
(1, 5, 7) みたいな多少妙な比率にしても、有理比である限りきれいな格子模様になる↓。(精度の問題でぼやけてるけど、理屈の上ではちゃんと点になる。)
http://ufcpp.net/study/csharp/fig/projection/1_5_7.png
(1, \sqrt 2, \sqrt 3) みたいに無理数比にしてしまうと、周期性がなくなる↓。
http://ufcpp.net/study/csharp/fig/projection/1_r2_r3_t10.png
↑は厚み(平面からの距離)10くらいまでをプロットしたもの。厚みを太くすればするほど、全体が真っ赤に染まるはず。逆に薄くすると↓みたいな感じ(厚み 1.2)。規則性があるようなないようなちょっと不思議な模様に。
http://ufcpp.net/study/csharp/fig/projection/1_r2_r3_t1.2.png
で、(2, 1 - \sqrt 5, 1 + \sqrt 5) みたいな比率にすると、x, y, z のうち2つだけが独立なので、縞模様になります(ある1方向には周期性がなくなって、別の1方向には周期性が残る)↓。
http://ufcpp.net/study/csharp/fig/projection/2_1-r5_1+r5.png
ちょっと数学的な話をすると、こういう、高次元の格子点を法線が無理数比の平面に投影するって話、準結晶の研究で出てきた話らしい。うまくやれば5回回転対称性を持つような準周期構造が作れるとか(結晶構造だと、1,2,3,4,6 回の回転対象性しか作れない)。