
# ellipseString.py
# Andrew Davison, ad@coe.psu.ac.th, Oct. 2025
'''
Animate a point around an ellipse and draw lines
from the point to the foci. Print the lengths of
the lines, and their sum, on the graph. The sum 
should be 2*semiMajor (10 in this case).

The ellipse is drawn using a polar equation.

Very similar to hyperbolaString.py
'''

import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation


def ellipse(theta):
  x = semiMajor * math.cos(theta)
  y = semiMinor * math.sin(theta) 
  return (x,y)


def initElems():
  # Initialize the animation elements
  dot.set_data([], [])   # the moving dot
  line1.set_data([], [])  # lines to the foci
  line2.set_data([], [])
  text1.set_text('')   # foci lengths
  text2.set_text('')
  textSum.set_text('') # sum of lengths
  return dot, line1, line2, text1, text2, textSum


def animate(i):
  # Update the animation for frame i
  theta = 2*math.pi * i/100 # Angle

  # Position of the dot on the ellipse
  xDot, yDot = ellipse(theta)
  dot.set_data([xDot], [yDot])

  # Line from dot to focal point 1
  x1Line = [xDot, foci1[0]]
  y1Line = [yDot, foci1[1]]
  line1.set_data(x1Line, y1Line)

  # Line from dot to focal point 2
  x2Line = [xDot, foci2[0]]
  y2Line = [yDot, foci2[1]]
  line2.set_data(x2Line, y2Line)

  # Calculate lengths and display them
  len1 = math.sqrt((xDot - foci1[0])**2 + \
                   (yDot - foci1[1])**2)
  len2 = math.sqrt((xDot - foci2[0])**2 + \
                   (yDot - foci2[1])**2)
  sumLen = len1 + len2

  # Positions for the labels
  pos1 = (xDot + foci1[0]) / 2, (yDot + foci1[1]) / 2
  pos2 = (xDot + foci2[0]) / 2, (yDot + foci2[1]) / 2
  posSum = (xDot-1, yDot+0.25)

  text1.set_position(pos1)
  text1.set_text(f'{len1:.2f}')
  text2.set_position(pos2)
  text2.set_text(f'{len2:.2f}')

  # also show the sum of the lengths
  textSum.set_position(posSum)
  textSum.set_text(f'Sum: {sumLen:.2f}')

  return dot, line1, line2, text1, text2, textSum


# ellipse parameters
semiMajor = 5  # axes lengths
semiMinor = 3
focalDist = math.sqrt(semiMajor**2 - semiMinor**2)
foci1 = (-focalDist, 0)
foci2 = (focalDist, 0)

# the figure and axes
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-(semiMajor + 1), semiMajor + 1)
ax.set_ylim(-(semiMajor + 1), semiMajor + 1)
ax.set_aspect('equal', adjustable='box')
ax.grid(True)
ax.set_title("Animation of a Dot on an Ellipse with Focal Lines")

# Plot the ellipse
thetas = [2*math.pi * i/100 for i in range(101)]
coords = [ellipse(theta) for theta in thetas]
ax.plot(*zip(*coords), 'b-', label='Ellipse')

# Plot the focal points
ax.plot(foci1[0], foci1[1], 'ro', markersize=8, 
                             label='Focal Pts')
ax.plot(foci2[0], foci2[1], 'ro', markersize=8)

# create the animation elements
dot, = ax.plot([], [], 'go', markersize=10, 
                         label='Animated Dot')
line1, = ax.plot([], [], 'r--')
line2, = ax.plot([], [], 'r--')
text1 = ax.text(0, 0, '', fontsize=12)
text2 = ax.text(0, 0, '', fontsize=12)
textSum = ax.text(0, 0, '', fontsize=12)

# start the animation
anim = FuncAnimation(fig, animate, 
                frames=100, interval=50,
                blit=True, init_func=initElems)
plt.show()