Update: The explanation of the rectilinear motion of the eight points in this animation is given here.
Fermat's library, @fermatslibrary
, posted on twitter a gif https://twitter.com/fermatslibrary/status/862659602776805379 illustrating
eight points moving inside a circle. No explanation of the motion was given. Here I reproduce it via Python Plotly.
Following each particular point one notices that it moves on a diameter of the greater circle.
The white rays intersect the circle (of radius R=1) at the $16^{th}$ roots of unity. The eight points lie at each time during their motion on a circle of radius 1/2, and the center at the middle of a ray of the greater circle.
If at the time t=0, corresponding to the initial frame, the smaller circle has the center $(0.5, 0)$, and radius r=1/2, then the points at this time are represented by the complex numbers $u[k]=0.5+0.5\cdot e^{2\pi j k/8}$, where $e^{2\pi j k/8}$, $k=\overline{0,7}$, are the $8^{th}$ roots of unity. The $m^{th}$ frame of the animation displays the position of points obtained from the initial ones, as follows:
In [1]:
import numpy as np
from numpy import pi
import plotly.graph_objects as go
In [2]:
u = np.array([0.5 + 0.5*np.exp(2*k*np.pi*1j/8) for k in range(8)], dtype=np.complex)
Define data that will be updated by each animation frame:
In [4]:
fig = go.Figure(go.Scatter(
x = u.real,
y= u.imag,
mode='markers',
marker=dict( size=0.01, color='white'),
name='moving_pts',
))
In [5]:
frames=[]
for m in range(64):
w = np.exp(-1j*m*pi/8)*0.5+np.exp(1j*m*pi/8)*(u-0.5)
frames.append(go.Frame(data=[go.Scatter(
x= w.real,
y=w.imag,
marker=dict(size=15, color='white')
)],
traces=[0]))
fig.update(frames=frames);
Set the plot layout:
In [6]:
axis = dict(visible=False,
range=[-1.1, 1.01],
autorange=False)
fig.update_layout(
title='Math is Fun',
width=600,
height=600,
showlegend=False,
xaxis=axis,
yaxis=axis,
hovermode='closest',
updatemenus=[dict(type='buttons',
showactive=False,
y=1,
x=1.2,
xanchor='right',
yanchor='top',
pad=dict(l=10),
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=150, redraw=False),
transition=dict(duration=80),
fromcurrent=True,
mode='immediate'
)]
)]
)]
);
The black disk is defined as a Plotly shape, and the withe diameters as quadratic Bézier curves defined by three colinear control points:
In [7]:
z = np.array([np.exp(2*k*np.pi*1j/16) for k in range(16)], dtype=np.complex) # the 16^th roots of unity
In [8]:
fig.add_shape(dict(type= 'circle',
layer= 'below',
xref= 'x',
yref='y',
fillcolor= 'rgba(10,10,10, 0.9)',
x0=-1.01,
y0= -1.01,
x1= 1.01,
y1= 1.01,
line_color= 'rgba(10,10,10, 0.9)'))
#define the shapes for the eight diameters
for k in range(8):
x0 = z[k].real
y0 = z[k].imag
x1 = z[k+8].real
y1 = z[k+8].imag
fig.add_shape(dict(type= 'path',
path= f'M{x0}, {y0} Q 0.0, 0.0 {x1}, {y1}',
line= dict(color= 'white',
width=0.75)
))
In [9]:
from plotly.offline import download_plotlyjs, init_notebook_mode, iplot
init_notebook_mode(connected=True)
iplot(fig)
In [10]:
from IPython.core.display import HTML
def css_styling():
styles = open("./custom.css", "r").read()
return HTML(styles)
css_styling()
Out[10]:
In [ ]: