I'm addicted to the following Sin-Cos-t 3D animation that came to Twitter 3 days ago, so I will summarize it. I wanted to draw a more interesting Lissajous figure with a light glue that seems to be an ordinary Lissajous figure. .. .. I was addicted to it. https://twitter.com/i/status/1302164499139502080 I wanted to draw the same picture, but I gave up at the following points.
・ Axes3D animation ・ Sin-Cos-t animation -Call the update () function directly to draw ・ Shift the Z axis ・ Rotation of figures ・ 3D Lissajous figure
At first, even though it was 3D, I finally thought that it would be possible to go with something like a reference. 【reference】 [Matplotlib basic] Try to write a dynamic graph ♬ ~ Video output; Gif animation In fact, in sin-cos-t you will find code similar to google: 【reference】 ② [Matplotlib] Visualization of 3D data
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#Add figure
fig = plt.figure(figsize = (8, 8))
#Added 3D Axes
ax = fig.add_subplot(111, projection='3d')
#Set the title of Axes
ax.set_title("Helix", size = 20)
#Set axis label
ax.set_xlabel("x", size = 14)
ax.set_ylabel("y", size = 14)
ax.set_zlabel("z", size = 14)
#Set axis scale
ax.set_xticks([-1.0, -0.5, 0.0, 0.5, 1.0])
ax.set_yticks([-1.0, -0.5, 0.0, 0.5, 1.0])
#Definition of pi
pi = np.pi
#Number of parameter divisions
n = 256
#Create parameter t
t = np.linspace(-6*pi, 6*pi, n)
#Spiral equation
x = np.cos(t)
y = np.sin(t)
z = t
#Draw a curve
ax.plot(x, y, z, color = "red")
plt.show()
The output is as follows, but I got a similar figure though it doesn't work. After that, just move it, so look for the animation. It's easy to find An animated plot in 3D. on matplotlib.org, but I found something closer below.
【reference】 3D animation using matplotlib This is almost the answer.
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
def gen(n):
phi = 0
while phi < 2*np.pi:
yield np.array([np.cos(phi), np.sin(phi), phi])
phi += 2*np.pi/n
def update(num, data, line):
line.set_data(data[:2, :num])
line.set_3d_properties(data[2, :num])
N = 100
data = np.array(list(gen(N))).T
line, = ax.plot(data[0, 0:1], data[1, 0:1], data[2, 0:1])
# Setting the axes properties
ax.set_xlim3d([-1.0, 1.0])
ax.set_xlabel('X')
ax.set_ylim3d([-1.0, 1.0])
ax.set_ylabel('Y')
ax.set_zlim3d([0.0, 10.0])
ax.set_zlabel('Z')
ani = animation.FuncAnimation(fig, update, N, fargs=(data, line), interval=10000/N, blit=False)
#ani.save('matplot003.gif', writer='imagemagick')
plt.show()
However, when I try to draw three animations, it looks like this:
aniy = animation.FuncAnimation(fig, update, N, fargs=(datay, liney), interval=10000/N, blit=False)
anix = animation.FuncAnimation(fig, update, N, fargs=(datax, linex), interval=10000/N, blit=False)
aniz = animation.FuncAnimation(fig, update, N, fargs=(dataxy, linexy), interval=10000/N, blit=False)
With this method, you can get similar animations pictorially, but I can't think of a way to save these three animations together.
I decided to plot each of them together with plt.pause (0.001) as shown below and save the figure. Finally, I decided to generate gif_animation from those figures.
frames =100
s=180
for n in range(frames):
s+=1
num = s%800+200
update(num,datax,linex)
update(num,datay,liney)
update(num,dataz,linez)
plt.pause(0.001)
plt.savefig('./sin_wave/'+str(n)+'.png'.format(elev, azim))
from PIL import Image,ImageFilter
images = []
for n in range(frames):
exec('a'+str(n)+'=Image.open("./sin_wave/'+str(n)+'.png ")')
images.append(eval('a'+str(n)))
images[0].save('./sin_wave/sin_wave_el{}_az{}.gif'.format(elev, azim),
save_all=True,
append_images=images[1:],
duration=100,
loop=0)
And by limiting the drawing range with line.set_data (data [: 2, num-200: num]) and num = s% 800 + 200 as in the code below, the result is closer to what you want. ..
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
def genxy(n,fx,fy,fxc=0,fyc=0):
phi = 0
sk = 0
while phi < 360:
yield np.array([fx*np.cos(phi)+fxc, fy*np.sin(phi)+fyc,phi])
phi += 36*5/n
def update(num, data, line):
line.set_data(data[:2,num-200 :num]) #Limit the drawing section
line.set_3d_properties(data[2,num-200 :num])
N = 960
datay = np.array(list(genxy(N,fx=0,fy=1,fxc=-2,fyc=0))).T
liney, = ax.plot(datay[0, 0:1], datay[1, 0:1], datay[2, 0:1])
datax = np.array(list(genxy(N,fx=1,fy=0,fxc=0,fyc=2))).T
linex, = ax.plot(datax[0, 0:1], datax[1, 0:1], datax[2, 0:1])
dataz = np.array(list(genxy(N,fx=1,fy=1,fxc=0,fyc=0))).T
linez, = ax.plot(dataz[0, 0:1], dataz[1, 0:1], dataz[2, 0:1])
# Setting the axes properties
ax.set_xlim3d([-2.0,2.0])
ax.set_xlabel('X')
ax.set_ylim3d([-2.0, 2.0])
ax.set_ylabel('Y')
ax.set_zlim3d([36.0, 80.])
ax.set_zlabel('Z')
frames =36
s=180
for n in range(frames):
s+=1
num = s%800+200
update(num,datax,linex)
update(num,datay,liney)
update(num,dataz,linez)
plt.pause(0.001)
plt.savefig('./sin_wave/'+str(n)+'.png'.format(0, 0))
from PIL import Image,ImageFilter
images = []
for n in range(frames):
exec('a'+str(n)+'=Image.open("./sin_wave/'+str(n)+'.png ")')
images.append(eval('a'+str(n)))
images[0].save('./sin_wave/sin_wave_el{}_az{}.gif'.format(0, 0),
save_all=True,
append_images=images[1:],
duration=100,
loop=0)
Let's rewrite the update () function as follows to shift the Z axis. As a result, the Z-axis direction of the image is drawn at the same position. The axis display is also erased here.
def update(num, data, line):
line.set_data(data[:2,num-200 :num])
line.set_3d_properties(data[2,num-200 :num])
ax.set_zlim3d([0+data[2,num-200], 34+data[2,num-200]])
ax.set_zticklabels([])
ax.grid(False)
Well, if you bring the coordinate axes to that arrangement, it is completed for the time being. However, this was difficult. First, you can rotate the shaft as follows. In the following, the picture of the initial value is output with the axis changed.
for elev in range(0,360,10): #Change of elevation
for azim in range(-180,180,5): #Change azimuth
ax.view_init(elev, azim) #Adjust the orientation of the coordinate axes here
frames =1 #34
s=180
for n in range(1): #frames
s+=1
num = s%800+200
update(num,datax,linex)
update(num,datay,liney)
update(num,dataz,linez)
plt.pause(0.001)
plt.savefig('./sin_wave/'+str(n)+'{}{}.png'.format(elev, azim))
As a result, that arrangement was not obtained.
You don't have enough imagination.
I can't think of anything other than the above angle range.
However, I came up with the idea of changing the axis headings for X-Y-Z.
yield np.array([phi,fx*np.cos(phi)+fxc, fy*np.sin(phi)+fyc])
It is said.
In other words, if you change the axis heading, the spiral will lie down next to you.
So I arrived at the following code.
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
def genxy(n,fx,fy,fxc=0,fyc=0):
phi = 0
while phi < 360:
yield np.array([phi,fx*np.cos(phi)+fxc, fy*np.sin(phi)+fyc])
phi += 36*5/n
def update(num, data, line):
line.set_data(data[:2,num-200 :num])
line.set_3d_properties(data[2,num-200 :num])
ax.set_xlim3d([0+data[0,num-200], 36+data[0,num-200]])
ax.set_xticklabels([])
ax.grid(False)
N = 960
datay = np.array(list(genxy(N,fx=0,fy=1,fxc=-2,fyc=0))).T
liney, = ax.plot(datay[0, 0:1], datay[1, 0:1], datay[2, 0:1])
datax = np.array(list(genxy(N,fx=1,fy=0,fxc=0,fyc=-2))).T
linex, = ax.plot(datax[0, 0:1], datax[1, 0:1], datax[2, 0:1])
dataz = np.array(list(genxy(N,fx=1,fy=1,fxc=0,fyc=0))).T
linez, = ax.plot(dataz[0, 0:1], dataz[1, 0:1], dataz[2, 0:1])
# Setting the axes properties
ax.set_xlim3d([20, 70.0])
ax.set_xlabel('Z')
ax.set_ylim3d([-2.0, 2.0])
ax.set_ylabel('X')
ax.set_zlim3d([-2.0, 2.0])
ax.set_zlabel('Y')
elev=20.
azim=35.
ax.view_init(elev, azim)
frames =100
s=180
for n in range(frames): #frames
s+=1
num = s%800+200
update(num,datax,linex)
update(num,datay,liney)
update(num,dataz,linez)
plt.pause(0.001)
plt.savefig('./sin_wave/'+str(n)+'.png'.format(elev, azim))
from PIL import Image,ImageFilter
images = []
for n in range(frames):
exec('a'+str(n)+'=Image.open("./sin_wave/'+str(n)+'.png ")')
images.append(eval('a'+str(n)))
images[0].save('./sin_wave/sin_wave_el{}_az{}.gif'.format(elev, azim),
save_all=True,
append_images=images[1:],
duration=100,
loop=0)
In this way, the 3D animation posted first was output.
However, what I originally wanted to do was a 3D Lissajous figure, so I'll try some.
You can do this by changing the following and changing frames = 200
to connect smoothly.
yield np.array([phi,fx*np.cos(3*phi/2)+fxc, fy*np.sin(2*phi/3)+fyc])
X : Y = 1 : 2 X : Y = 1/2 : 2/3 X : Y = 3/2 : 2/3
・ I drew a 3D animation of Sin-Cos-t. ・ You can now draw 3D animation ・ I drew a 3D Lissajous figure
Completed version Improved to draw a Lissajous figure at the origin 3d_animation_Risajyu/3D_animation_risajyu.py
point added
Recommended Posts