Grasshopper でルービックキューブを作成してみました。python や C#は使用せず、Anemone というプラグインを使用しております。インストールしていない方は
からインストールできますので、インストールしてから望んでください。難易度的にはデータツリーを理解していないとかなり苦しいかと思います。Panel コンポーネントでデータの中身を確認しながら、進めていただければと思います。こちらのチュートリアルは動画化しております、動画の方がよい方は以下のリンクからどうぞ!また、完成版の grasshopper file を github に push しましたので、必要であれば
からダウンロードできます。
概要
上画像がプログラムの全体像となっております。各ブロックごとに、説明していこうと思います。
各キューブの中心点を作成
- Square コンポーネントで2 × 2のグリッドを作成し、頂点を取得します。
- Linear Array コンポーネントで取得した頂点を Z 方向に複製します。3 点 ×3 点 ×3 点= 27 点生成されたかと思います。これを各キューブの中心点としていきます。
回転させる点を囲う Box を生成
- XY 平面・YZ 平面・ZX 平面、それぞれ 3 面回転させる面があるかと思います。1 面を回転させるのに、9 ブロックを回転させていくかと思いますが、それぞれグループ化していきます。
- List Item コンポーネントで適当に、中心とその両隣の 3 点を取得します。Index 番号で[10, 13, 16]番目を取得しています。
- もう 1 つ List Item コンポーネントを用意し、全体の中心点、Index 番号で 13 番目を取得します。
- Center Box コンポーネントで取得した 3 点を中心とした Box を 3 つ生成します。0.3×0.3×3 の Box を生成しております。
- Rotate コンポーネントを 2 つ用意し生成した 3 ボックスを回転させます。上画像の様に、9 ボックス生成します。各ボックス内に、回転させる際の 9 点が入っている状態です。
Stream Filter で生成した Box を選択化
- 先ほど生成した Box を List Item コンポーネントで取り出します。
- Stream Filter コンポーネントで Box を選べるようにします。Number Slider コンポーネントの値によって、選べるようになってます。
- Custom Preview コンポーネントと Colour Swatch コンポーネントで色付けしておきましょう。透明に色を付けたものを使用しております。
選んだ Box に対する回転平面を取得
- Stream Filter コンポーネントで選んだ Box に対して、回転させる平面を取得していきます。上画像の例ですと、XY 平面に広い面が向いている Box はルービックキューブ全体の中心とする XY 平面に平行に回転させていくかと思います。なので、Stream Filter コンポーネントを更に用意し、選んだ Box に対して平行な平面を出力するようにしていきます。
- Plane Origin コンポーネントで出力した平面の中心点を、ルービックキューブの中心点とした平面を取得します。
ブロックの生成
- 次に、ブロックを生成していきます。Center Box コンポーネントで最初に生成した 27 点を中心とした Box を生成し、各ブロックを作成します。各辺は 0.48 として、隣り合うブロック同士が若干隙間が空くようにしております。
- Deconstruct Brep コンポーネントで各ブロックを分解し面を取得後、Area コンポーネントで面の中心点を取得します。
面を色ごとにグループ化
- 面を色ごとにグループ化していきます。6 面(赤・青・白・黄・緑・オレンジ)+外部に面しない(黒)の 7 色 7 グループに分けていきます。
- Deconstruct コンポーネントで Area コンポーネントで取得した各面の中心点の座標を XYZ 成分に分けます。
- Bounds コンポーネントで XYZ 成分の最大値と最小値のドメインを取得し、Deconstruct Domain コンポーネントで最大値・最小値を取得します。
- Member Index コンポーネントで最大値・最小値がある Index 番号を取得し、List Item コンポーネントで面のリストから該当する Index 番号を取得することで、各色に該当する面を取得できます。
- Merge コンポーネントで Member Index コンポーネントから取得した、黒以外の面がある Index 番号をまとめ、Cull Index コンポーネントでそれを引くことで外部に面しない黒部分の面を取得できます。
- Entwine コンポーネントでそれぞれの色ごとにまとめます。
色付け
- Simple Mesh コンポーネントで各面をメッシュ化します。
- Mesh Colours コンポーネントで色付けしていきます。それぞれの色を Entwine コンポーネントでまとめ、接続していきます。Mesh Colours コンポーネントの出力端子は Flatten しておきます。
各ブロックごとにブランチ化
- 各ブロックの 6 面でブランチをまとめていきます。Area コンポーネントで各面の中心点を取得します。
- Member Index コンポーネントで、上で取得した面の中心点が、以前取得した面の中心点の並びだと何番目なのかを取得します。
- List Item コンポーネントで Mesh Colours コンポーネントから取得できる各面を Member Index コンポーネントから取得できる Index 番号をつなぐことで、以前取得した面の中心点の並びで面を並べ替えることができます。
- Partition List コンポーネントで 6 面ずつブランチ化することで、ブロックごとにブランチ化されます。
(※この項目非常にわかりにくいかと思います。Panel コンポーネントでデータの中身を確認しながら進めてみてください。)
各ブロックの中心点を取得
- 各ブロック中心点を取得します。Area コンポーネントで各面の中心点を取得します。
- List Item コンポーネントで適当に、向かいあう 2 点を取得します。
- 取得した 2 点を足して 2 で割れば中心点が取得できます。
ブロックを回転させる処理の実装

- anemone の Loop Start /Loop End コンポーネントを用意しつなぎます。
- ループさせる値を接続します。D0 に各ブロックの中心点・D1 に色付けしたメッシュを、D2 には 0 を入力しておきます、この値は後々使用します。
- まずは各ブロックの中心点から、以前作成した Box(上画像の薄い水色の直方体)の中に納まる点を取得します。以前作成した Stream Filter コンポーネントから、回転させたい面の Box を選択します。
- Point は Graft・Simplify しておきます。Point in Brep コンポーネントで選択した Box の内部に点が属しているかを Bool 値で取得します。※Point in Brep コンポーネントに接続している Brep コンポーネントには、Stream Filter コンポーネントの出力端子から得られる、Box が接続されております。
- Dispatch コンポーネントで、Box 内部の点とそれ以外を分けます。
- 内部に属する点のみ、Rotate コンポーネントで回転させます。入力端子 A の角度については次項で説明します。入力端子 P は回転中心となる平面なので、以前作成した Plane Origin コンポーネントから取得してきます。
- Merge コンポーネントで回転させた点とさせなかった点をまとめます。
- 余計な null を含んでいるので、Clean Tree コンポーネントで null を削除して、D0 に返してあげます。
- 続いてブロックを回転させていきます。
- D2 を Mesh コンポーネントにつなぎ、Simplify しておきます。Split Tree コンポーネントから、回転させる 9 つのブロックを取得していきます。
- Point in Brep コンポーネントから、回転させる点が Bool 値で取得できてました。これを Flatten して Member Index コンポーネントに接続し、True の Index 番号を取得し、その番号のブランチが回転させるブロックのブランチとなります。Construct Path コンポーネントで Path を取得し、Split Tree コンポーネントに接続することで、回転させたいブロックだけが取得できます。
- 先ほどと同様に Rotate コンポーネントで回転させ、Merge コンポーネントで回転させたものとさせなかったものをまとめ、D1 に返してあげます。
回転角の作成
- 上画像の緑色になっている Number Slider コンポーネントで回転させていき、緑色になっている Panel コンポーネントから実際に回転させる角度が出てくるようになっております。やや複雑なので、とりあえずプログラムを組んでから説明をしていきます。
- Multiplication コンポーネントで回転させるための Number Slider コンポーネントに 10 を掛けていきます。回転させるための Number Slider コンポーネントは‐36 ~ 36 まで移動できるようになっております。10 を掛けているのは 10 度ずつ回転させるためです。この掛け算により、-360 ~ 360 までの値が取得できます。この値を D2 に返してあげます。
- D2 の値に、+ 180 度、-180 度して Construct Domain コンポーネントでドメインを生成します。これを Source に接続します。Target には‐180 度~ 180 度までのドメインを接続します。
- 先ほど Multiplication コンポーネントで取得した値を Remap Numbers コンポーネントでリマップします。ここから取得できる Result が実際に回転させる角度になるので、Rotate コンポーネントの入力端子 A に接続します。
Anemone で組んだ Loop 処理の解説
とりあえず必要な変数をまとめます。
- 上画像の 1): selectBlockToRotate の Number Slider コンポーネントは、上画像の薄い透明で水色の直方体で、どの面を回転させるかを選ぶためのものです。
-
- : rotateBlock の Number Slider コンポーネントを左右に移動させると、ブロックが回転しますが、その横の Panel コンポーネントで実際何度回転させているかの値が出てきます。
-
- : nextRotate の Number Slider コンポーネントは、1 手終了ごとに、1 増やしていきます。これは、Loop Start コンポーネントの N 端子に接続されています。
- resetGame の Button コンポーネントは Loop Start コンポーネントの Trigger に接続されており、Game をリセットするためのものです。
『Anemone での Loop 処理を解説していきます。』
- まず初めに回転させる面を選びます。回転させるべくブロックとその中心点が取得できます。
- rotateBlock のスライダーを移動させることで、実際回転していきます。例えば、90 度回転させたとしましょう。
- D0・D1 には指定した面が 90 度回転され Loop Start に戻されます。D2 には、90 度が Loop Start に返されます。
- nextRotate を1増やすことで、次の Loop に手動で移行させます。この時、D0 には回転させた状態の点・D1 には回転させた状態のブロック・D2 には回転させた角度 90 度が入力されている状態です。
- 次の Loop で再度回転させる面を選び、1 手目で回転させた状態の点から回転させる点・ブロックを抽出し回転させ D0・D1 に返します。
- この時、回転させる角度に工夫する必要があります。D2 からは、1 回目に回転させた 90 度が出力されている状態で、これを直接 Rotate コンポーネントのアングルに接続すると、回転する面を動かすと、選ぶ度に 90 度回転されてしまいます。なので、前回入力した角度の際に 0 度の時が 0 度となり、その角度から相対的に何度回転させるかという処理を加えないといけません。
- 相対的な角度を算出するために、D2 から取得してきた角度に ±180 度し、それを―180 度~ 180 度にすることで、1 回目の入力 90 度を 0 度とし、そこから更に回転させていきます。
これの繰り返しとなります。文章では中々理解できないかと思いますので、1 手 1 手確認しながら進めてみてください。
完成
以上になります。かなりわかりにくい箇所あったかと思います。他にもやり方はいろいろあるかと思います。そもそも Grasshopper でなくていいじゃんって意見もあるかと思います。自分なりの方法でも実装してみてください。