Blender の Scripting で三葉結び目を描く

こんにちは｜こんばんは。カエルのアイコンで活動しております @kyamaz :frog: です。

## はじめに
本稿は、数学的な対象を Blender の Scripting を使って扱うエントリです。
:frog: は結び目や絡み目に興味があり、書籍『[絵で学ぶ　結び目理論　ーこの紐、ほどけますか？ー](https://www.ohmsha.co.jp/book/9784274232473/)』（新庄玲子・田中心著, オーム社, 2024）をゆっくり読んでいます。これには結び目の図が多く描かれており、タイトル通り「絵で学ぶ」ことができるように工夫されています。この本のように結び目をうまく解説するには、結び目を絵に描くための軽快なツールがほしいところです。
しかし、:frog: は適切なツールの心当たりがありません。強いて言えば、Blender か Processing かというところかと思います。
そこで、本稿では、結び目で必ず最初の方に紹介される「三葉結び目」を Blender を使って描いてみましょう。

## Blender で三葉結び目を描く

次の方針で、三葉結び目を描いてみましょう。

* ベジェ曲線で描く
* ３方向に対称性があるため，_Control Point_ は3の倍数である9個を置く
* `(x, y)` は，極座標 $(r _{\theta}\cos{\theta}, r _{\theta}\sin{\theta})$ で考える
* $\theta$ は，三葉を描いた絵を考えると2周回って描いていると考えると都合がよい
* $r _{\theta}$ は，$\theta$ として2周回っているうちに周期が3回になるように考える

この方針によりプログラムを書いてみます。

```python: TrefoilBezier.py
import bpy
import math

# 既存オブジェクトの削除
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

# パラメータの設定
num_points: int = 9
scale: float = 1.0
r1: float = 2.0
r2: float = 1.5

# Bezier 曲線の作成
curve_data = bpy.data.curves.new('TrefoilBezier', type='CURVE')
curve_data.dimensions = '3D'
spline = curve_data.splines.new('BEZIER')
spline.bezier_points.add(num_points - 1)  # num_points - 最初の1点

# 三葉結び目の近似座標（num_points=8等分したパラメータ点）の計算
for i in range(num_points):
    t: float = i / num_points * 2 * math.pi
    x: float = (r1 + r2 * math.cos(3 * t)) * math.cos(2 * t)
    y: float = (r1 + r2 * math.cos(3 * t)) * math.sin(2 * t)
    z: float = math.sin(3 * t)
    p = spline.bezier_points[i]
    p.co = (scale * x, scale * y, scale * z)
    p.handle_left_type = 'AUTO'
    p.handle_right_type = 'AUTO'

# 曲線を閉じる
spline.use_cyclic_u = True

# 曲線に太さをつける
curve_data.bevel_depth = 0.1
curve_data.bevel_resolution = 5

# 曲線オブジェクトの作成
curve_obj = bpy.data.objects.new('TrefoilBezier', curve_data)
bpy.context.collection.objects.link(curve_obj)
```

_Scripting_ のワークスペースに切り替えて、_Text Editor_ にて上記のプログラムを記述します。_Run Script_ すると、次の図のような「三葉結び目」のオブジェクトが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136950/bca74853-5cbb-427c-8a73-091078e76345.png)

射影図が3点で交わる非自明な結び目は、左手系、右手系の鏡像を区別をしなければ、この三葉結び目が唯一となります。鏡像のもう一方は、プログラムのなかで `z: float = - math.sin(3 * t)` と Z 軸を負号にすると得られます。（下図）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136950/65ed5759-9653-47a0-a97b-b1ed24f7e9b2.png)


## おわりに
いかがでしたでしょうか。

参考書籍『絵で学ぶ　結び目理論　ーこの紐、ほどけますか？ー』には、射影図が4交点から9交点までが一覧で紹介[^ref_katlabs]されています。WikiPedia「結び目 (数学)」[^ref_knot]でも3交点から7交点までの図が紹介されています。

この一覧にあるような結び目も作成したくなると思います三葉結び目のように対称性があるものは数式で記述できそうですが、そうでないものも多いです。そのような数式を使って計算で求めなくても、本稿で紹介した方法に倣って、コントロールポイントを上手く3次元の点列として配列で定義すれば、一覧にある結び目も立体的に作成することができそうです。
いまの :frog: は取り組めていませんが、いずれどこかで試してみたいと思います。

最後に、本稿を書いたときの環境（特にBlenderのバージョン）を明記して締めさせて頂きます。

<details><summary>本稿の環境</summary>
本稿のために使用した環境は以下となります。

macOS: Sequoia 15.4.1 (chip: Apple M1)
Blender: 4.3.2
PYTHON INTERACTIVE CONSOLE 3.11.9 [Clang 15.0.0]
</details>

ご一読いただきまして有り難うございます。
（●）（●） Happy Hacking!
/""　＿＿""＼


[^ref_knot]: https://ja.wikipedia.org/wiki/%E7%B5%90%E3%81%B3%E7%9B%AE_(%E6%95%B0%E5%AD%A6)

[^ref_katlabs]: https://katlas.org/wiki/The_Rolfsen_Knot_Table によると「Knots with 9 crossings」は49個ありますが、参考書籍『絵で学ぶ〜』の第一版では37個しか一覧で紹介されていません。ご注意ください。（投稿後に追記：2025/05/07）