先日、Eテレ「ミミクリーズ」で、「あのもよう」という回が放送されていたのを子と一緒に観た。
その中で、自然界に出現する不思議な模様として黄金比・黄金角によって作られる模様を紹介していた。 実際に木に模した棒に等間隔で等角度ずつずらして順番に葉をつけていくと、どのような模様になるか、といったシミュレーションをしていて面白い。
用語としては「葉序 (Phyllotaxis)」というらしい。
これをブラウザ上で同じようにシミュレーションできるものがあると便利だな、と思い作ってみることにした。
成果物
3Dで木の幹(植物の茎)と、まわりに等間隔で伸びる葉が描画され、マウスやタッチ操作で回転/拡大縮小させることができる。 葉の数、そして葉と葉の間の角度(開度 と呼ぶらしい?)を変更するとリアルタイムで描画が更新される。
プリセットの角度として、黄金角(約137.5° = 360 * (1 - 1 / Φ)
)や、キレイに重なる π/4 などを選択できる。
少しずつ動かしてみると色々な模様と変化を観測できて意外と楽しい。
開発
使っているのは Vite + React + TypeScript, Three.js, あと Tailwind。
フロントエンド不慣れな自分でも、ChatGPTのおかげで3時間くらいで作れた。ありがたい。
とにかく3D空間内に葉を薄い円柱で表現して配置していく。角度をθとすると、n番目の葉の位置は (r * cos(θ * n), r * sin(θ * n), n * h)
といった感じで求められる。あとは高さに合わせて半径r
をちょっと調整したり。
Reactによって inputの値が変更された場合にそれを反映する。どうやるのかな、と思ったがカメラや光源やrendererはそのまま変えずに済むよう、useRef
で THREE.Scene
を保持しておいて、そこから children を一度削除して新しいパラメータで再配置、という方法で実現できた。 オブジェクトの数が変わらない場合は座標や角度を変更するだけにする、といった最適化もできたかもしれないけど、まあいいか。
ただ削除は簡単ではなく、ループで回しながら条件にあるものを remove()
していくと何故か消え残るものがあったり、まとめて消すようにしてみても何度も削除&再配置を繰り返すとメモリがどんどん枯渇していってフリーズしたり、といった問題にぶつかった。結局削除する場合はただ scene
から remove()
するだけでなく、それぞれ geometry
や material
を明示的に disopose()
する必要があったようだ。
そのあたりを改善した結果、スマホでもサクサク動くようになったと思う。