はじめに

Homeデザイン > Twisting Tower

Twisting Tower −立体をPythonでつくる

Pythonスクリプト

Shadeには,Pythonというプログラム言語が組み込まれています。このPythonをスクリプトとして使用することによって,図形を生成することが出来ます。スクリプトというのは,プログラム言語によって記述された一連の手順のことです。スクリプトによってコンピュータに手順を指示すれば,コンピュータが手順にしたがって仕事をします。

Shadeのスクリプトは,Pythonの文法にしたがって記述されなければなりません(Pythonの文法は他のプログラム言語(C言語やBasic言語)と類似しています)。また,Shade独自のコマンドもあります。

 

Pythonスクリプトで描く三角形と四角形

最初に,スクリプトがどのようなものかを確認しましょう。

[メニュー]>[表示]>[スクリプト]のチェックをONにして,「スクリプト」ウィンドウを表示させましょう(図1)。

図1 「スクリプト」ウィンドウ

 

次に,「スクリプト」ウィンドウの「記録」のチェックをONにしましょう(図2)。

図2 スクリプトの記録

 

正面図に「原点を中心として,底辺の長さが2000,高さが1000の二等辺三角形」を描いてみましょう(図3)。[ツールボックス]>[Create]>[閉じた線形状]を選択し,正面図で,3つの点= (1000,0,0)ー(0,0,-1000)ー(-1000,0,0) を順番にクリックし,最後に最初の点= (1000,0,0) をクリックして図形を閉じてください。

図3 正方形の描画

 

「スクリプト」ウィンドウの中に,以下のようなスクリプトが表れます。このスクリプトが 「3つの点= (1000,0,0)ー(0,0,1000)ー(-1000,0,0) を結ぶ閉じた線形状」を描く手順です。

xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -1000], None, None, None, None)
xshade.scene().append_point([-1000, 0, 0], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

スクリプトには文法があります。このスクリプトの文法は以下のように解釈できます。

  1. 「xshade.scene().begin_creating()」(1行目)と「xshade.scene().end_creating()」(7行目)で挟まれた部分に描画をする手順が記される。
  2. 「xshade.scene().begin_line(None, 1) 」(2行目)から「xshade.scene().end_line() 」(6行目)が線形状を描くコマンド。
  3. 「xshade.scene().append_point([x, y, z], None, None, None, None)」(3〜5行目)が線形状の頂点。

「xshade.scene()」とか「None」が何を意味するかなどはわかりにくいと思いますが,わからない部分は「おまじない」と見なして,とりあえずはわかる部分を理解しましょう。

スクリプトを変更してみましょう。 「スクリプト」ウィンドウの「記録」のチェックをOFFにしてください。スクリプトの5行目の下に以下の6行目を挿入してください(1〜5行目と7行目以降に変更はありません)。

xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -1000], None, None, None, None)
xshade.scene().append_point([-1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, 1000], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

 

先ほど描いた閉じた線形状を削除して,「スクリプト」ウィンドウの「実行」ボタンを押してください。正方形が描かれるはずです(図4)。

図4 正方形の描画

 

Twisting Tower

以上のスクリプトをベースに,以下のような「Twisting Tower」=「ねじれる立体」をモデリングしましょう(図5)。

Twinting Tower A

図5 Twisting Tower

スクリプトでコピー

描いた四角形をコピーしましょう。描いた四角形を選択し,もう一度,「スクリプト」ウィンドウの「記録」チェックをONにします(図6)。

図6 スクリプト

 

#四角形を作成
xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -1000], None, None, None, None)
xshade.scene().append_point([-1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, 1000], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

#四角形をコピー
(カーソル)

 

ところで,pythonでは「#」を使ってコメントを記述することになっています。すなわち,スクリプトの各行における「#」以下はコメントです。コメントというのは人間のメモのことで,コンピュータはコメントを無視します。コメントにはプログラム言語の文法に無関係なメモを記入できます。スクリプトにコメントを加えることで,スクリプトの内容が理解しやすくなります。これまでのスクリプトのはじめに「#四角形の描画」といったコメントを書きましょう。また,これまでのスクリプトの最下行に「#四角形をコピー」といったコメントを書き,カーソルはその下の行にもっていきましょう。なお,pythonでは空行を自由に使うことができます。空行を追加して,スクリプトを見やすくしましょう。

さて,[スクリプト]ウィンドウの[記録]チェックがONになっていることを確認し,また,先に描画した[閉じた線形状]が選択されていることを確認しましょう。そして,[ツールボックス]>[作成]>[複製]>[数値入力]を選択して,[上面図]の[原点]をクリックしましょう(必ず[上面図]の[原点]をクリックしてください)。すると,以下のような[トランスフォーメーション]ダイアログが現れます(図7)。図7に示したように,[拡大縮小]=(1.0, 1.0, 1.0),[回転]=(0,15,0),[直線移動]=(0, 200, 0) を入力して,[OK]をクリックしてください。

図7 トランスフォーメーション

 

[拡大縮小]は元図形(選択された図形)のスケールの変化(伸縮)を指示する数値です。(1.0, 1.0, 1.0) ならば,コピー図形(コピーされる図形)のスケールは元図形と同一です。[回転]は元図形をXYZの3軸に対してどれだけ回転するかを指示する数値です。(0,5,0)ならば,コピー図形は,Y軸(すなわち鉛直軸)に対して5度,回転します。[直線移動]は元図形の移動距離を指示する数値です。(0, 200, 0) ならば,コピー図形は,Y軸(すなわち鉛直軸)方向に200,移動します。すなわち,上記の指示によって,コピー図形は,5度回転し上方に200移動した図形,となります。

図8 図形のコピー

 

スクリプトは以下のように記録されていると思います。

#四角形を作成
xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -1000], None, None, None, None)
xshade.scene().append_point([-1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, 1000], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

#四角形をコピー
xshade.scene().copy_object([0, 0, 0], [1, 1, 1], [0, 5, 0], [0, 200, 0])

 

追記された12行目が図形をコピーするコマンドです。このコマンドの文法は以下の通りです。

copy_object([基点], [拡大縮小], [回転], [直線移動])

[基点]は,[ツールボックス]>[copy]>[数値入力...]を選択した後にクリックする点を意味します。図形はこの[基点]を中心に回転されます。

 

コピーを繰り返し

次に,このコピーを複数回繰り返すようスクリプトを書きましょう。

[スクリプト]ウィンドウの[記録]チェックをOFFにし,スクリプトの12行目以降を以下のように書き換えましょう。

#四角形を作成
xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -1000], None, None, None, None)
xshade.scene().append_point([-1000, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, 1000], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

#四角形を10回コピー
for i in range(10):
→ xshade.scene().copy_object([0, 0, 0], [1, 1, 1], [0, 5, 0], [0, 200, 0])

 

ここで,上記の「→」は TAB(インデント)を意味します。「→」の部分はキーボードの「tab」を入力して,行頭をインデント(右にずらす)してください。プログラム言語によっては(たとえば,一般的なC言語でもBasic言語では),行頭を自由にインデントできるのですが,pythonでは行頭のインデントが文法的な意味をもちます。また,12行目の「for n in range(10):」の文末に「:」(ダブルコロン)が付いていることにも注意してください。

12行目の「for i in range(10):」は,この行に続くインデントされた行を10回繰り返すことを意味します。「(10)」は「i」の値が「0〜9」まで1ずつ増加することを意味します。上記のスクリプトでは「n」の値は意味をもちませんから,「for i in range(10):」は単純な10回繰り返しを意味します。

すべての図形を削除し,スクリプトを[実行]してみてください。スクリプトは最初に元図を描き,次々に「5度回転し上方に200移動した図形」を10個,描画します。この図形を[レンダリング]すると,以下のようになります。

Twisting Tower B

図9 繰り返しコピー

 

スクリプトのパラメトリック化

図9の図形に「ある操作」を加えれば,図5のような「なめらかにねじれる曲面」になるのですが,その「ある操作」については後述することにして,「変数を用いて」スクリプトを書き換えましょう。「変数を用いて」図形の特徴(大きさや高さやねじれの状態など)を記述すると,図形のカタチを操作できるようになります。変数を用いて操作できる図形は「パラメトリックな図形」と呼ばれます。

ここまでのスクリプトを以下のように書き換えましょう。ここでは,2〜6行目に変数を記述し,11行目以降の数値を変数に置き換えています。

#変数
d = 1000 #四角形の中心から頂点までの距離(対角の長さ÷2)
r = 5   #角度の増分
h = 200  #高さの増分
n = 10   #コピー回数
s = 1.0  #拡大縮小の比率

#四角形を作成
xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([d, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -d], None, None, None, None)
xshade.scene().append_point([-d, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, d], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

#四角形をn回コピー
for i in range(n):
→ xshade.scene().copy_object([0, 0, 0], [s, 1, s], [0, r, 0], [0, h, 0])

 

pythonは自由に変数を記述できます。プログラム言語によっては「変数は定義(宣言)してから使う」といった仕様になっているものがありますが,pythonでは,いきなり「d = 1000」といった記述が可能です。「d = 1000」という文によって,数値変数である「d」が生成され「1000」が代入されます。

上記のスクリプトでは,「コピー」コマンドである「copy_object([基点], [拡大縮小], [回転], [直線移動])」の [拡大縮小] に変数「s」を使って,[s, 1.0, s] を指定しています。このコマンドによって,コピーされる図形の平面形(上から見たカタチ)は元図形の s 倍に [拡大縮小] されます。

試しに,d(四角形の中心から頂点までの距離,すなわち,対角の長さ÷2), r(角度の増分),h(高さの増分),n(コピー回数),s(拡大縮小の比率)の値を変えて,図形を描画してみてください(図10)。

Figure 10

図10 Twisting Tower

 

自由曲面

スクリプトができてしまえば,繰り返しの回数を増やしたり,図形の特徴を操作することができるようになります。それが,「アルゴリズミック・デザイン=カタチの自動生成」の威力だと思います。

さて,shade には,「自由曲面」というおもしろい機能があります。ここまでにできた立体を「自由曲面」をつかって,スムースに「ねじれる立体」に仕上げましょう。スクリプトをつかって「自由曲面」をつくることもできるのですが,自動化(アルゴリズミック化)しなければならないほど面倒な手順ではないので,ここでは,手作業で「自由曲面」をつくることにします。

shadeの図形は[ブラウザ]によって管理されています。[ブラウザ]には10個(n個)の四角形が表れているはずです(図11)。

図11 ブラウザ

 

[ブラウザ]の[ルートパート]をクリックし,[ツールボックス]>[パート]>[自由曲面]をクリックしてください。すると[ブラウザ]に[自由曲面]という[パート]が現れます(図12)。

図12 自由曲面の作成

 

[パート]というのは図形を格納するフォルダ(引き出し)です。通常の[パート]は図形を階層化して整理するために使われますが,[自由曲面]は特別な[パート]として,連続する図形の周囲に面を張ります。[ブラウザ」上ですべての[閉じた線形状]を選択し(複数の図形を選択する場合には Shift キー,または,command キーを使います),[自由曲面]の中に(上に)ドラッグしてください(図13)。[自由曲面]の前に記号「▼」が現れ,[自由曲面]パートの中に[閉じた線形状]が格納されます。ここで,記号「▼」をクリックすると,[自由曲面]パートが折りたたまれて,内部の[閉じた線形状]が非表示になります。

図13 自由曲面

 

この状態で[レンダリング]をすると,[自由曲面]が現れます(図14)。

Figure 14

図14 自由曲面のレンダリング

 

[自由曲面]は連続する図形の周囲に面を張る操作なので,図形そのものは存在しなくなります。そのため,図14の立体では,最上部に穴が空いています。最上部に穴が空いているのは変なので,穴を埋めましょう。

[ブラウザ]で,[自由曲面]の中にある最上部の四角形([ブラウザ]では一番下にある四角形)を選択し,[ツールボックス]>[copy]>[数値入力...]を使って,同じ位置に図形をコピーしてください。同じ位置へのコピーは,コピーの[数値入力...]の際に,[拡大縮小]= (1.0, 1.0, 1.0),[回転]= (0,0,0),[直線移動]= (0, 0, 0) を指定すればOKです(図15)。

図15 同位置にコピー

 

次に,[ブラウザ]上で,コピーした[閉じた線形状]をマウスでドラッグして,[自由曲面]の外に移動してください(図16)。

図16 図形の移動

 

これで,[自由曲面]とは別に,最上部に[閉じた線形状]が存在することになり,最上部の穴がふさがります(図17)。

Figure 17

図17 ねじれた立体(完成)

 

[自由曲面]の作成を含めて,ここまでのすべての手順をスクリプト化すると以下のようになります。

#変数
d = 1000  #四角形の中心から頂点までの距離(対角の長さ÷2)
r = 5    #角度の増分
h = 200   #高さの増分
n = 10   #コピー回数
s = 0.9   #拡大縮小の比率

#自由曲面を作成
xshade.scene().create_surface_part(None)

#四角形を作成
xshade.scene().begin_creating()
xshade.scene().begin_line(None, 1)
xshade.scene().append_point([d, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, -d], None, None, None, None)
xshade.scene().append_point([-d, 0, 0], None, None, None, None)
xshade.scene().append_point([0, 0, d], None, None, None, None)
xshade.scene().end_line()
xshade.scene().end_creating()

#四角形をn回コピー
for i in range(n):
→ xshade.scene().copy_object([0, 0, 0], [s, 1, s], [0, r, 0], [0, h, 0])

#最上段の四角形を同じ位置にコピー
xshade.scene().copy_object([1200, 0, -550], [1, 1, 1], [0, 0, 0], [0, 0, 0])

#コピーした四角形を自由曲面から分離
xshade.scene().place_parent(1)
xshade.scene().place_sister(0)

 

スクリプトの改造

ここまでに手順を追ってスクリプトを作成する方法を解説してきました。最後に,四角形を任意の多角形に変更できるようにスクリプトを改造しましょう。いや,改造というより,最初からスクリプトを書き直してみましょう。

任意の多角形を扱うには,三角関数が必要になります。pythonでは,三角関数などの数学関数は「math」というクラスによって定義されています。数学関数を使う場合は,スクリプトの冒頭(1行目)に「import math」と記述することになっています。三角関数を使って,「ねじれる立体」のスクリプトをより一般化すると,以下のように記述できます。

import math
scene = xshade.scene()  #インスタンス

#変数
d=20000    #半径
dscale=0.4   #最上階の比率
fheight=4000  #階高
nfloor=30   #階数
div=4      #頂点数
rot=5      #ひねり角度

#変数の計算
dd = d * (1.0 - dscale) / nfloor   #半径の増分
r = 360.0 / div
num = 0

#自由曲面の作成
scene.create_surface_part("Tower")

#平面図形の作成
scene.begin_creating()
for n in range(nfloor+1):
→ num = num + 1
→ scene.begin_line("Floor"+str(num), 1)
→ for p in range(div):
→ → dc = (d - dd * n) * math.cos(math.pi / 180.0 * (r * p + rot * n) )
→ → ds = (d - dd * n) * math.sin(math.pi / 180.0 * (r * p + rot * n) )
→ → scene.append_point([dc, n*fheight, ds], None, None, None, None)
→ scene.end_line()
scene.end_creating()

#最上階の図形を同じ位置にコピー
scene.copy_object([0,0,0], [1.0, 1.0, 1.0], [0, 0, 0], [0, 0, 0])

#最上階の図形を自由曲面から分離
scene.place_parent(1)
scene.place_sister(0)

 

このスクリプトでは,「コピー」コマンド(四角形をn回コピー)を使わないで,3次元空間上の座標を計算して,図形(四角形)を描いています。図形を描く高さは「回数×高さの増分」で簡単に計算できますから,「コピー」コマンドを使うより,この方が単純明快だと思います。また,四角形を多角形が描けるようにパラメーター化しています。

上記の「変数」は,超高層ビルを想定して,最下階の大きさとして半径=対角÷2が20m,階高にあたる高さの増分を4m,階数を30階としています。

2行目の「scene = xshade.scene()」は,表記のインスタンス(継承)を意味します。スクリプトの冒頭に「scene = xshade.scene()」と書くと,以降のスクリプトで「xshade.scene()」を「scene」と省略することができるようになります。

4行目以降で変数の初期設定をしていますが,変数の名前には任意の文字列が使用できます。たとえば,「dscale」は最下部の図形に対する最上部の図形の大きさの比率を表す変数です。この変数の名前は,単に一文字で「s」などとするより,半径(diameter)の比率(scale)という意味で「dscale」などとした方がスクリプトがわかりやすくなると思います。

このスクリプトでは,任意の頂点数の多角形(m角形)の頂点の座標を,図18に示したように,三角関数を用いて計算しています。また,「dscale」と「高さ」にしたがって,半径「d」を変化させています。なお,pythonでは,sin関数は「math.sin()」,cos関数は「math.cos()」,piを「math.pi」と記述することになっています。関数の扱う角度はラジアンなので,度を使う場合は「math.pi」を使って,度をラジアンに変換する必要があります。

Math

図18 m角形の描画

 

上記スクリプトでは,18行目「scene.create_surface_part("Tower")」で[自由曲面]を作成し,24行目「scene.begin_line("Floor"+str(num), 1)」以降で[閉じた線形状]を作成しています。ここで,18行目の「"Tower"」は[自由曲面]の名前,24行目の「"Floor"+str(num)」は[閉じた線形状]の名前を表しています。先のスクリプトではこの部分を「None」としていました。名前の指示する部分が「None」になっていると,shadeはデフォルト(初期)の名前である「自由曲面」や「閉じた線形状」を名前に使用します。

 

Curving Tower(その1:単純なシリンダー)

さて,Twisting Tower(ねじれる立体)からちょいと目先を変えて,Curving Tower(曲がる立体)をつくってみましょう(図19)。

最初に,図20のように,円を積み上げて,単純なシリンダー状の超高層ビルような立体をつくることを考えましょう。

図20 シリンダー

 

このスクリプトは以下のように書けます。簡単ですよね。

import math
scene = xshade.scene()  #インスタンス

#変数
d = 20000    #半径
fheight = 4000  #階高
nfloor = 30    #階数

#円の作成
for n in range(nfloor+1):
→ y = n * fheight
→ scene.create_disk(None, [0, y, 0], d, 1)

 

Curving Tower(その2:カーブ状に積み上げる)

このシリンダーを,図21のように,円弧状に曲げることを考えます。図21は曲げすぎていて,気味が悪い感じがしますが,曲げ具合は後で調整します。

図21 Curved Cylinder

 

図21では,図20の単純に積み上げたシリンダーの各層の円の中心のX座標を,図22に示したように,1/4円弧状にずらしています。

 

図22 Curving Tower の概念図

 

スクリプトは以下の通りで,9〜10行目を加えて,円の中心のX座標を計算しています。

import math
scene = xshade.scene()  #インスタンス

#変数
d=20000    #半径
fheight=4000  #階高
nfloor=30    #階数

#円の作成
for n in range(nfloor+1):
→ a = math.asin(1.0 * n / nfloor)
→ x = fheight * nfloor * (1.0 - math.cos(a))

→ y = n * fheight
→ scene.create_disk(None, [x, y, 0], d, 1)

 

9行目は,sin(a)=y/H であることから,a=sin-1(y/H) を計算しています。Y座標で表される高さ:y=n * fheight,円弧の半径である全体の高さ:H=nfloor * fheight ですから,a=sin-1(n / nfloor)になります。(n / nfloor)に1.0を掛けているのは,(n / nfloor)が実数であることを明示するためです。python は整数同士の計算結果を整数と見なしますので,ここでは割り算の結果が実数であることを明示する必要があります。

10行目で,円の中心のX座標は H - L で計算されます。L=H * cos(a)ですから,x=(fheight * nfloor) - (fheight * nfloor * cos(a))をくくって,上記のようにしています。

 

Curving Tower(その3:カーブと比率の調整)

カーブの曲がり具合と円の比率を調整できるようにスクリプトを書きかえます(図23)。

図23 カーブと比率の調整

import math
scene = xshade.scene()  #インスタンス

#変数
d=20000    #半径
fheight=4000  #階高
nfloor=30    #階数
dscale=0.4   #最上階の半径の比率
xscale=0.3   #曲げの比率

dd = d * (1.0 - dscale) / nfloor  #半径の増分

#円の作成
for n in range(nfloor+1):
→ a = math.asin(1.0 * n / nfloor)
→ x = fheight * nfloor * (1.0 - math.cos(a))

→ y = n * fheight
→ scene.create_disk(None, [x*xscale, y, 0], (d - dd * n), 1)

 

Twinting and Curving Tower

「Twisting Tower」のスクリプトに,「Curving Tower」のスクリプトを組み込むと,以下のようになります。スクリプトを書き加えているのは,X座標をずれを計算する2行を加えて,多角形の頂点のX座標にずれを加える点だけです。

import math
scene = xshade.scene()  #インスタンス

#変数
d=20000    #半径
fheight=4000  #階高
nfloor=30    #階数
div=4      #頂点数
rot=1      #ひねり角度
dscale=0.4   #最上階の比率
xscale=0.2   #カーブの比率

#変数の計算
dd = d * (1.0 - dscale) / nfloor  #半径の増分
r = 360.0 / div
num = 0

#自由曲面の作成
scene.create_surface_part("Tower")

#平面図形の作成
scene.begin_creating()
for n in range(nfloor+1):
num = num + 1
scene.begin_line("Floor"+str(num), 1)

a = math.asin(1.0 * n / nfloor)
x = fheight * nfloor * (1.0 - math.cos(a))

for p in range(div):
dc = (d - dd * n) * math.cos(math.pi / 180.0 * (r * p + rot * n) )
ds = (d - dd * n) * math.sin(math.pi / 180.0 * (r * p + rot * n) )
scene.append_point([x * xscale + dc, n*fheight, ds], None, None, None, None)
scene.end_line()
scene.end_creating()

#最上階の図形を同じ位置にコピー
scene.copy_object([0,0,0], [1.0, 1.0, 1.0], [0, 0, 0], [0, 0, 0])

#最上階の図形を自由曲面から分離
scene.place_parent(1)
scene.place_sister(0)

 

このスクリプトの描画結果を図24に示します。

図24 Twint & Curve