2023.02.28 やっと「らせんアニメ」ができました。これまで作ってきた「らせん」に比べ、アルゴリズムを分かりやすくしました。巻数=4 のサンプルアニメと、ソースコードを記します。
2023.03.01 いろいろ設定できるように改良しました。「代数らせんアニメ」描画ソフトの完成版(by Kero)です。
# spiral-algebraic.py
# らせんを巻いていく。
# まず、「代数らせん」(アルキメデスのらせん) 2023.03.01 by Kero
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
# 描画画面設定。
fig, ax = plt.subplots(figsize=(8,2))
ax.set_aspect('equal') # 正方形の方眼紙。
artists = [] # パラパラ漫画設定。最初は白紙。
# 回転のマトリックス。
# 座標の列ベクトル群に左から掛けると図が原点中心に角度rdだけ右回転。
def A(rd):
Am = np.array([[np.cos(rd), -np.sin(rd), 0],
[np.sin(rd), np.cos(rd), 0],
[0., 0., 1.]])
return Am
# x軸で反転させるマトリックス。
B = np.array([[1,0,0],[0,-1,0],[1,1,1]])
# 「代数らせん」関数(中心からの距離が等間隔 = 回転角と曲率半径が正比例)
# 原点に対して、回転角 ki、曲率半径 con*ki、比例定数 con の点を生成。
def rasenA(con, ki, r00):
r = con * ki + r00
x = r * np.sin(ki)
y = r * np.cos(ki)
z = np.array([[x],[y],[1]]) # らせんにこの新しい点を追加。
return z
# らせんを描く準備。
su = input("何回巻きますか?:")
r0 = input("半径初期値 : ")
co = input("線間隔 : ") # 線間は等間隔。代数らせん特有項目。
xm = input("x軸長さ(空リターンで自動設定) : ")
ym = input("y軸長さ(空リターンで自動設定) : ")
suu = float(su) # 巻数の文字列から実数に変換。
tmk = 2 * np.pi * suu # トータルの巻角度。
kzm = tmk / 100 # トータル巻角度のキザミを100個とする。
r00 = float(r0) # 半径初期値。
con = 0.5 * float(co) / np.pi # 代数らせんの線間隔は等間隔。
mak2 = 0. # 回転した長さ。最初はゼロ。
ras = np.array([[0],[0],[1]]) # 原点を中心に「原らせん」を生成。
title = "Archimedean spiral. turns= " + su + ", first radius= " + r0 + ", line space= " + co
# らせんを描く。
for ki in np.linspace(0, tmk, 100):
# 【代数らせん】= rasenA
z = rasenA(con, ki, r00)
ras = np.append(ras, z, axis=1) # 原らせんに新たな点を追加。
Am = A(ki) # アフィン変換の回転マトリックスを作る。角度ki右回転。
ras2 = np.dot(Am, ras) # かいてーん!!
z2 = ras2[:, -1:] # らせん終端(最外側)座標を取り出す。
ras3 = ras2 - z2 # 終端を原点に平行移動。
mak2 = mak2 + (con * ki + r00) * kzm # 回転した長さを積立て貯金。
mak3 = np.array([[mak2],[0],[0]]) # それをベクトルにして。
ras4 = ras3 + mak3 # 回転した長さ分だけ右に平行移動。
ras5 = np.dot(B,ras4) # x軸で反転。
ras6 = np.delete(ras5, 0, axis=1) # らせんの最初の座標を削除。
artist= ax.plot(ras6[0], ras6[1], c='b') # グラフ表示。
artists.append(artist) # 一コマをパラパラ漫画の1ページに収録。
# xy軸の設定。入力値を調べる。数値以外なら自動設定。
if xm.isnumeric():
xmax = float(xm)
else:
xmax = np.max(ras5[0]) * 1.1
if ym.isnumeric():
ymax = float(ym)
else:
ymax = np.max(ras5[1]) * 1.1
plt.xlim(0,xmax) # x軸の長さは、最終らせんのx最大値の1割増し。
plt.ylim(0,ymax) # y軸の長さは、最終らせんのy最大値の1割増し。
plt.text(0, ymax * 1.1, title)
# 以下はアニメの常套句。temakizushi.gif に保存。
anim = ArtistAnimation(fig, artists, interval=50, repeat=True)
anim.save('spiral-algebraic.gif', writer='pillow')
plt.show()
plt.close()
sys.exit()