
# rollCycloid.py
# Andrew Davison, ad@coe.psu.ac.th, Dec. 2025
'''
Roll a circle of radius a along the x-axis and draw the cycloid.
'''


import math
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from frange import linspace


def update(val):
  theta = math.radians(tSlider.val)
  Circle.set_xdata(xsCircleFn(theta))  # translate the circle
  
  ts = linspace(0, theta, max(2, int(50 * theta)))
  xs, ys = zip( *[cycloid(t, a) for t in ts])
  Cycloid.set_data(xs, ys)

  x, y = cycloid(theta, a)
  Point.set_data([x], [y])

  fig.canvas.draw_idle()


def cycloid(t, a):
  x = a * (t - math.sin(t))
  y = a * (1 - math.cos(t))
  return (x, y)


# -----------------------------------
a = 1  # radius of rolling circle

# create coordinates for the circle
ts = linspace(0, 2 * math.pi, 100)
xsCircleFn = lambda t: [t + math.cos(ti) for ti in ts]
              # translate the x-coords by an offset of t
ysCircle = [1 + math.sin(ti) for ti in ts]  # unchanging

# Setup the plot
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
plt.ylim(0, 3)
plt.xlim(-1, 1 + 4*math.pi)
plt.gca().set_aspect('equal')
plt.grid('on')

# X-axis pi ticks and labels (minor)
pi_xticks = [k * math.pi for k in range(5)]
ax.set_xticks(pi_xticks, minor=True)

ax.tick_params(axis="x", which="minor")
for k, x in enumerate(pi_xticks):
  label = f"{k}π"
  if k == 0:
    label = ""
  elif k == 1:
    label = "π"
  ax.text(x, -0.1, label, color="red",
    transform=ax.get_xaxis_transform(),
    ha="center", va="top")


# Plot the initial shapes
theta = 0
Circle, = plt.plot(xsCircleFn(theta), ysCircle, 'k')
x, y = cycloid(theta, a)
Cycloid, = plt.plot([x], [y], 'r', ms=3)
Point, = plt.plot([x], [y], 'ro', ms=5)

# create slider
ax = plt.axes([0.18, 0.33, 0.67, 0.02])
tSlider = Slider(ax, 'angle', 0, 720, valstep=5, 
                 valinit=theta)
tSlider.on_changed(update)
# make slider active at startup
tSlider.set_val(tSlider.valinit)

plt.suptitle("A Cycloid (a=1) made with a Rolling Circle")

plt.show()
