[Introduction to Matplotlib] Axes 3D animation: I played with 3D Lissajous figures ♬

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.

What i did

・ Axes3D animation ・ Sin-Cos-t animation -Call the update () function directly to draw ・ Shift the Z axis ・ Rotation of figures ・ 3D Lissajous figure

・ Axes3D animation

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. Figure_helix.png It's easy to find An animated plot in 3D. on matplotlib.org, but I found something closer below.

・ Sin-Cos-t animation

【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()

matplot003.gif 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.

-Call the update () function directly to draw

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. .. sin_wave_el0_az0.gif

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)    

・ Shift the Z axis

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)

sin_wave_el0_az0.gif

・ Rotation of figures

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.

・ 3D Lissajous figure

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 sin_wave1-2_el20.0_az35.0.gif X : Y = 1/2 : 2/3 sin_wave1w2-2w3_el20.0_az35.0.gif X : Y = 3/2 : 2/3 sin_wave3w2-2w3-2_el20.0_az35.0.gif

Summary

・ I drew a 3D animation of Sin-Cos-t. ・ You can now draw 3D animation ・ I drew a 3D Lissajous figure

bonus

Completed version Improved to draw a Lissajous figure at the origin 3d_animation_Risajyu/3D_animation_risajyu.py

point added sin_wave_point_el20.0_az35.0.gif

Recommended Posts

[Introduction to Matplotlib] Axes 3D animation: I played with 3D Lissajous figures ♬
[Introduction to Pytorch] I played with sinGAN ♬
[Introduction to StyleGAN] I played with "The Life of a Man" ♬
[Introduction to sinGAN-Tensorflow] I played with the super-resolution "Challenge Big Imayuyu" ♬
[Introduction to RasPi4] I played with "Hiroko / Hiromi's poisonous tongue conversation" ♪
[Introduction to StyleGAN] I played with "A woman transforms into Mayuyu" ♬
How to title multiple figures with matplotlib
[Introduction to AWS] I played with male and female voices with Polly and Transcribe ♪
[Introduction to StyleGAN] I played with style_mixing "Woman who takes off glasses" ♬
Animation with matplotlib
Animation with matplotlib
I want to display multiple images with matplotlib.
I wanted to visualize 3D particle simulation with the Python visualization library Matplotlib.
I played with wordcloud!
[Introduction to system trading] I drew a Stochastic Oscillator with python and played with it ♬
[Introduction to Pytorch] I tried categorizing Cifar10 with VGG16 ♬
I want to manually create a legend with matplotlib
3D plot with matplotlib
[Python] How to create a 2D histogram with Matplotlib
[Introduction to AWS] I tried playing with voice-text conversion ♪
Adjust axes with matplotlib
[Python] I want to make a 3D scatter plot of the epicenter with Cartopy + Matplotlib!
[IOS] GIF animation with Pythonista3. I was addicted to it.
I made an animation to return Othello stones with POV-Ray
Introduction to RDB with sqlalchemy Ⅰ
Introduction to Nonlinear Optimization (I)
I read "Reinforcement Learning with Python: From Introduction to Practice" Chapter 1
I wanted to solve the ABC164 A ~ D problem with Python
[Introduction] I want to make a Mastodon Bot with Python! 【Beginners】
I read "Reinforcement Learning with Python: From Introduction to Practice" Chapter 2
Create plot animation with Python + Matplotlib
Introduction to RDB with sqlalchemy II
I played with PyQt5 and Python3
I want to do ○○ with Pandas
I played with Mecab (morphological analysis)!
Easy to draw graphs with matplotlib
I want to debug with Python
Easy animation with matplotlib (mp4, gif)
(Matplotlib) I want to draw a graph with a size specified in pixels
I tried fMRI data analysis with python (Introduction to brain information decoding)