Amendo
Amendo

Reputation: 89

hypocycloid animation matplotlib - python

I'm trying to make this animation from wikipedia, however, I'm having a hard time figuring out how to do it. Basically I need to do two things:

  1. Plot all the graphs in one single frame.
  2. Plot the biggest circle and keep it, and then plot the smallest circle and the hypocycloid points. However, I also need to keep the points of the hypocycloid and renew the position of the smallest circle.

this is the code I have, but I get ten different frames

import numpy as np
import matplotlib.pyplot as plt
R=3 #Biggest circle radius =3
r=1 #smallest circle radius =1
t=np.linspace(0, 2*np.pi,100) # values from zero to 360 degrees
i=0

xc1=R*np.cos(t) #biggest circle
yc1=R*np.sin(t) #biggest circle
plt.plot(xc1,yc1)

while i<=2*np.pi:
    x=(R-r)*np.cos(i)+r*np.cos((R-r)*i/r) #x values of the hypocycloid
    y=(R-r)*np.sin(i)-r*np.sin((R-r)*i/r)#y value of the hypocycloid
    plt.plot(x,y)  
    i+=2*np.pi/10 

    plt.show()

Thank you very much in advance.

Upvotes: 0

Views: 1509

Answers (1)

Thomas K&#252;hn
Thomas K&#252;hn

Reputation: 9810

Here a full example how to do it, using the equations given in the Wikipedia page the OP linked. As there are quite a few variables to be tracked, I thought it was best to collect everything in a class.

from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np


class Hypocycloid:

    def __init__(self, ratio = 3, frames = 100, ncycles = 1):
        self.frames = frames
        self.ncycles = ncycles
        self.fig, self.ax = plt.subplots()
        self.ax.set_aspect('equal')

        ##big circle:
        theta = np.linspace(0,2*np.pi,100)
        x = np.cos(theta)
        y = np.sin(theta)

        self.big_circle, = self.ax.plot(x,y,'b-')

        ##small circle:
        self.small_r = 1./ratio
        r = self.small_r
        x = r*np.cos(theta)+1-r
        y = r*np.sin(theta)
        self.small_circle, = self.ax.plot(x,y,'k-')

        ##line and dot:
        self.line, = self.ax.plot([1-r,1],[0,0],'k-')
        self.dot, = self.ax.plot([1-r],[0], 'ko', ms=5)
        ##hypocycloid:
        self.hypocycloid, = self.ax.plot([],[],'r-')


        self.animation = FuncAnimation(
            self.fig, self.animate,
            frames=self.frames*self.ncycles,
            interval=50, blit=False,
            repeat_delay=2000,
        )


    def update_small_circle(self, phi):
        theta = np.linspace(0,2*np.pi,100)
        x = self.small_r*np.cos(theta)+(1-self.small_r)*np.cos(phi)
        y = self.small_r*np.sin(theta)+(1-self.small_r)*np.sin(phi)
        self.small_circle.set_data(x,y)


    def update_hypocycloid(self, phis):
        r = self.small_r
        x = (1-r)*np.cos(phis)+r*np.cos((1-r)/r*phis)
        y = (1-r)*np.sin(phis)-r*np.sin((1-r)/r*phis)
        self.hypocycloid.set_data(x,y)

        center = [(1-r)*np.cos(phis[-1]), (1-r)*np.sin(phis[-1])]

        self.line.set_data([center[0],x[-1]],[center[1],y[-1]])
        self.dot.set_data([center[0]], [center[1]])

    def animate(self, frame):
        frame = frame+1
        phi = 2*np.pi*frame/self.frames
        self.update_small_circle(phi)
        self.update_hypocycloid(np.linspace(0,phi,frame))

hypo = Hypocycloid(ratio=3.25, frames = 40, ncycles=4)

##un-comment the next line, if you want to save the animation as gif:
##hypo.animation.save('hypocycloid.gif', writer='imagemagick', fps=10, dpi=75)

plt.show()

The parameters that you can pass to the Hypocycloid class are ratio (radius of the small circle relative to the big one), frames (number of frames for one trip of the small circle around the big circle), and ncycles (number of trips). The final result for ratio=3.25, frames=40, and ncycles=4 looks like this:

result of above code

Upvotes: 2

Related Questions