正多面体の切断

正多面体を平面で切断したときの切断面を描くCindyアプリケーションを作ります。

わかりやすいところで、中学でも学ぶ、立方体の断面を考えます。次のような図です。

まず設計をしましょう。

設計

(1) 正多面体は rpolyhedron() で面データを取得して描く。(色塗りなし)

(2) 辺のリストを作る。

(3) 正多面体の各辺と切断する平面の交点を求める。

(4) (3)で求めた交点を結んだ図をplate3d() で描く。

では,順にやっていきましょう。

(1) 正多面体の面データを取得して描く。

rpolyhedron() は面を塗った多面体を描くので,オプション "nodisp" をつけて,戻り値の面データを取得し,その第1要素の頂点リストを使ってVertexEdgeFace() で描きます。Nohiddenbyfaces() で陰線処理もしましょう。なお,後の都合があるので,1辺を2としたら,原点が中に入らないよう,中心を(2,2,2)になるように平行移動しておきます。

fd=rpolyhedron("1",6,2*sqrt(3),["nodisp"]);

pd=transfer3d(fd_1,["Trans=[2,2,2]"]);

VertexEdgeFace("1",[pd,fd_2]);

Nohiddenbyfaces("1","phe3d1","phf3d1",["dr,2"],["do"]);

(2) 辺のリストを作る。

println(pd);

とすると,頂点リストが確かめられます。

[[0,0,4],[4,0,4],[4,4,4],[0,4,4],[0,0,0],[4,0,0],[4,4,0],[0,4,0]]

とコンソールに表示されます。

fd の第2要素は面を表すデータです。これも println(fd_2) で表示してみましょう。

[[4,3,7,8],[4,8,5,1],[4,1,2,3],[6,2,1,5],[6,5,8,7],[6,7,3,2]]

立方体の6つの面を表しています。それぞれの数は,頂点の番号です。

この面を表すデータを用いて,12個の辺のリストを作ります。

まず,面ごとに隣り合う2つづつをとって辺データを作ります。

辺のリストを sidelist とします。1つの面について4つの辺データを作り,これを6つの面についておこないます。

sidelist=[];

repeat(length(fd_2),s,

sd=append(fd_2_s,fd_2_s_1);

repeat(length(sd)-1,t,

v1=sd_t;

v2=sd_(t+1);

sidelist=append(sidelist,[pd_v1,pd_v2]);

);

);

ここで,3行目で sd=append(fd_2_s,fd_2_s_1); としているのは,始点を末尾に追加しているのです。

たとえば,面を表すデータが [1,2,3,4] のとき,辺は [1,2],[2,3],[3,4],[4,1] の4つになります。この4番目のペアを取得するためです。

これで24個のデータができます。(println(sidelist) で表示できます。)辺の数は12ですが、各辺とも2回ずつ数えているので24個になるのです。

これを整理してもいいですが、面倒なので、そのままにしましょう。平面との交点を調べるときに、同じものが2つづつできることになりますが、そのあとで処理します。

(3) 正多面体の各辺と切断する平面の交点を求める。

切断する平面Sは、適当に3点を決めて作ります。ひとまず,[6,0,0],[0,6,0],[0,0,6] としておきます。

この3点で決まる平面と線分の交点は、interps()で求められます。辺リスト sidelist のそれぞれに対して、この関数で交点を求め、それをリストにします。sidelist の要素のそれぞれに処理を適用するので apply()が便利です。交点リストは intpt としましょう。(intersection point)

interps()は交点がない場合は[i,i,i]を返してきますので、[i,i,i]でなければ intpt に追加していきます。

p1=[6,0,0];

p2=[0,6,0];

p3=[0,0,6];

intpt=[];

repeat(length(sidelist),s,

pt=interps(p1,p2,p3,sidelist_s_1,sidelist_s_2);

if(pt!=[i,i,i],intpt=append(intpt,pt));

);

intpt=sort(intpt);

println(intpt); で結果を表示してみましょう。

[[2,4,0],[0,4,2],[0,4,2],[0,2,4],[0,2,4],[2,0,4],[4,0,2],[2,0,4],[2,4,0],[4,2,0],[4,2,0],[4,0,2]]

となっています。同じものが2つずつあります。そこで,sort() で並べ替えて,1つおきにとっていくことにします。たとえば次のようにします。

plist=[];

repeat(length(intpt),s,

if(isodd(s),plist=append(plist,intpt_s));

);

isodd(s) は sが偶数かどうかの判定です。sはrepeatで繰り返されるときの番号ですから、偶数ならplistに追加(append)していくことになります。

さて、これで、断面の頂点リスト

[[0,2,4],[0,4,2],[2,0,4],[2,4,0],[4,0,2],[4,2,0]]

ができました。交点6つの座標のリストです。

(4) (3)で求めた交点を結んだ図をplate3d() で描く。

Cindyscript の convexhull3d() を使うと,この点のリストに対して面データを作ることができます。convexhull3d()は,凸型多面体の頂点リストから面データを作るものです。いまの3個の頂点は同一平面上にありますが、これに1つ点を追加して凸型多面体にすると、それに対して面データを作ってくれます。原点が中に入らないようにしましたので,追加する点を原点にします。

pd0=convexhull3d(prepend([0,0,0],plist));

pd=[];

forall(pd0_2,if(!contains(#,1),pd=append(pd,#)));

先ほど得た plist の前に prepend() で [0,0,0] を追加します。こうすれば、交点がいくつでも,常に原点が1番になります。

convexfull3d() の戻り値をpd0 とします。pd0の第2要素が面データのリストです。

こんどは、この面データの全てを走査するのに forall() を使います。リスト pd0_2 の要素を最初から順に走査します。調べている要素は # で表されます。その中に 1 が含まれていなければ (!contains() !は否定)ppに追加します。実際にできるのは1つだけです。

最後にこの面を表す頂点の順序に従って、多角形の頂点リストを作ります。plist から指定された番号の点を順に拾っていきます。

pp=[];

repeat(length(pd_1),s,

nn=pd_1_s;

pp=append(pp,plist_(nn-1));

);

poly3d("1",pp,["Color=blue"]);

plate3d("1",pp,["Color=yellow","Alpha=0.4","Rayoff"]);

とすれば,黄色で断面が塗られます。

また、断面を斜線で示すなら、

ret=poly3d("1",pp,["Color=blue"]);

hatch3d("1",ret);

とします。

スライダでインタラクティブに切る

正多面体を切る平面をスライダで設定します。3点を決めればよいのでどのようにやってもよいでしょう。次は一例です。

まず、スライダを作ります。Cinderellaの作図ツールで、線分と線分上の点を取ります。線分の位置を工夫すれば、線分上の点だけを使って3点を決めることができます。

先ほど暫定的に決めた p1,p2,p3 の代わりに、このスライダで点を設定します。たとえば

p1=[G.y*2,0,0];

p2=[0,H.y*2,0];

p3=[0,0,K.y*2];

とします。

必要に応じて,頂点に名前をつけましょう。このページの先頭の図は名前をつけたものです。

立方体以外の正多面体

はじめに描くのを立方体以外の正多面体にします。たとえば

fd=rpolyhedron("1",4,2,["nodisp"]);

pd=transfer3d(fd_1,["Trans=[1,1,1]"]);

とすると正四面体の断面,

fd=rpolyhedron("1",12,2,["nodisp"]);

pd=transfer3d(fd_1,["Trans=[1,1,1]"]);

とすると,正十二面体の断面ができます。

さらに、切っている平面を多面体の外まで大きめに描くことも考えられます。いろいろ工夫ができるでしょう。

戻る