今回は再帰処理を使用して、上画像の様な球に沿った曲線を描いていこうと思います。Anemoneというプラグインを使用したケースとPythonを使用したケース2種類紹介しております。是非ご参考にしてください。Pythonの基礎については割愛しておりますが、非常に基本的な内容となっております。Anemoneはこちらからインストールできます。こちらのチュートリアルは動画化しておりますので、動画の方がよい方は以下のリンクからどうぞ!
概要(Anemoneを使用したケース)

上画像がAnemoneを使用した場合のプログラム全体像となっております。
各ブロックごとに解説していきます。
球面に点を作成

- Sphereコンポーネントで原点(0,0,0)半径100mmの球を作成します。
- Populate Geometoryコンポーネントで作成した球面に点を発生させます。今回は2000点発生させました。
- List Itemコンポーネントで発生させた点から0番目の点を取得します。
Anemoneを使用して再帰処理を実装

- Loop Start ,Loop End コンポーネントを用意しつなぎます。Loop Startの入力端子TにはButtonコンポーネントを接続し、繰り返し回数Nには、発生させた点の数から1を引いた1999を入力します。
- Loop StartとLoop Endを拡大し、入力する値をD0~D2まで用意します。D0には発生させた点から抽出した0番目の点を接続、D1は空のブランチを生成したいので何も接続しません。D2には発生させた2000点を接続します。
- Closest Pointコンポーネントで2000点のなかから、D0に入力された点に最も近い点を抽出しLoop EndのD0に値を返してあげます。
- Cull Indexコンポーネントで2000点の中から探し出したD0に最も近い点を削除し、Loop EndのD2にリストを返してあげます。
- D1から空の値を抽出しDataコンポーネントに接続し空のブランチを作成します。
- Insert Itemコンポーネントで空のブランチに先ほど探し出したD0に最も近い点をブランチに挿入しLoop EndのD1にリストを返してあげます。
- Buttonコンポーネントを押して繰り返し処理を開始します。繰り返し処理が終われば、Loop Endの出力端子D1から2000点のリストが取得できますが、0番目にNullが入ってしまっているかと思います。Clean TreeコンポーネントでNullを削除します。
- Nurbs Curveコンポーネントで取得した点のリストをもとにNurbs Curveを作成しいます。
- 最後にCustom PreviewコンポーネントとColour Swatchコンポーネントで色付けして完成です。
Anemoneを使用した場合:おまけ

- Anemoneはループの過程をプレビューしてくれるので、せっかくなので少しかっこよくします。Lineコンポーネントで球の原点とD0に最も近い点を直線で結びます。
- Perp Frameコンポーネントで作成した直線の終点に直行する平面を作成します。入力端子はReparametraizeして入力端子tに1.0を入力します。
- Circleコンポーネントで直線の端点に先ほど作成した平面に円を作成し、Surfaceコンポーネントでサーフェイスを張ります。
- Custom PreviewコンポーネントとColour Swatchコンポーネントで直線と円のサーフェイスに色付けします。
- Loop Startに接続したButtonコンポーネントを押せば、円が描かれる過程がプレビューできるかと思います。
- 以上でAnemoneのケースを終わります。
Pythonを使用したケース : while loop

import ghpythonlib.components as gh origin_point =gh.ConstructPoint(0,0,0) base_sphere = gh.Sphere(origin_point,100) number_of_cloudpoints = 2000 cloud_points = gh.PopulateGeometry(base_sphere,number_of_cloudpoints,1) food_point = cloud_points[0] infection_points = [] while len(infection_points) < number_of_cloudpoints: closest_point = gh.ClosestPoint(food_point,cloud_points) cloud_points = gh.CullIndex(cloud_points,closest_point["cp_index"],True) infection_points.append(closest_point["closest_point"]) food_point = closest_point["closest_point"]
上のコードはPythonで先ほどの球に沿った曲線を描くための点を抽出しております。まずはwhile文を使用したケースから説明します。
- Grasshopper上でGhPython Scriptコンポーネントを用意し、ダブルクリックでエディターを開きます。
- 1行目でghpythonlib.componentsというモジュールをインポートしてghという名前にしておきます。これで普段grasshopperで使用しているコンポーネントを呼び出すことができます。
- 3行目で変数origin_pointに点(0,0,0)を代入します。この点が球の原点となります。
- 4行目で変数base_sphereに原点をorigin_point 半径を100mmの球を代入しています。
- 5行目は変数number_of_cloudpointsに球状に発生させる点の数を代入しています。
- 6行目で変数cloud_pointsにPopulateGeometoryでbase_sphere上にnumber_of_cloud_pointsの数だけ点を発生させて代入しています。第三引数の1はseed値になります。
- 7行目で変数food_point(最も近い点を探し出すための点)にcloud_points[0]で0番目の点を代入します。
- 8行目で探し出された点を格納するための空の配列infection_pointsを作成します。
- 10行目から繰り返し処理を書いていきます。while文でinfection_pointsのリストの数がnumber_of_cloudpointsの未満の間処理を繰り返しております。
- 11行目で変数closest_pointにfood_pointに最も近い点をcloud_pointsから算出し代入しています。
- 12行目cloud_pointsから探し出したclosest_pointを削除します。closest_pointは辞書型の値になっているので、closest_point["cp_index"]としてindex番号を取得しています。第三引数はindexをラップさせるかどうかを指定しますがTrueにしておきます。(※index番号を範囲外の値を指定した際にラップさせるかどうかという値になります。)
- ⒔行目で空のリストinfection_pointsに探し出したclosest_pointを挿入します。closest_pointは辞書型なので、closest_point["closest_point"]とすることで、Geometoryを取得します。
- 14行目で変数food_pointを探し出した点closest_pointに再代入します。
- 探し出すための点を定義(food_point)⇒点群(cloud_points)から近接点(closest_point)を取得⇒近接点(closest_point)を配列に格納し点群から削除⇒近接点の近接点をさらに探す・・・・といった処理になっております。

- Ghpython Scriptコンポーネントの出力端子をinfection_pointsとし、エディターのOKボタンを押します。
- Nurbs Curveコンポーネントで取得した点をもとにNurbs Curveコンポーネントを生成して完了です。
Pythonを使用したケース : 再帰関数

ついでに再帰関数で実装したケースも書いておきます。
import ghpythonlib.components as gh origin_point =gh.ConstructPoint(0,0,0) base_sphere = gh.Sphere(origin_point,100) number_of_cloudpoints = 2000 cloud_points = gh.PopulateGeometry(base_sphere,number_of_cloudpoints,1) food_point = cloud_points[0] infection_points = [] def loop(i): i-=1 global food_point global cloud_points closest_point = gh.ClosestPoint(food_point,cloud_points) cloud_points = gh.CullIndex(cloud_points,closest_point["cp_index"],True) infection_points.append(closest_point["closest_point"]) food_point = closest_point["closest_point"] if i: return loop(i) else: return True loop(number_of_cloudpoints)
- 基本的な流れは同じですが、今回はloopという関数を定義しております。引数にiをとり、関数を実行するごとにiから1を引いていきます。
- 関数内で記述している global food_pointとglobal cloud_pointsはglobal変数なので、global変数であることを宣言しています。
- if文で、もしiが0でない(True)とloop関数の引数にiを再代入して再度実行します。iが0(False)のとき、Trueを返して再帰処理を終えます。
- 最終行loop(number_of_cloudpoints)で作成したloop関数を実行しています。
以上になります。今回はAnemoneを使用して再帰処理を実行した場合とGHpythonを使用した場合を記載しました。是非ご参考にしてみてください。