Ray in Ellipse

Ray in Ellipse

import sys
import math


a, b = 4, 3.5
iteration = 10
focus_length = math.sqrt(a**2 - b**2)
origin, θ = (-focus_length, 0), 60


class EllipseRay(Scene):
  
  # ellipse: (x - h)**2 / a**2 + (y - k)**2 / b**2 = 1
  #    line: y = tan(θ) * x
  def find_intersection(self, a, b, h, k, θ):
    m = math.tan(math.radians(θ))
    D = 4*((h*b**2 + m*k*a**2)**2 - (b**2 + (m*a)**2) * ((h*b)**2 + (k*a)**2 - (a*b)**2))
    if D < 0:
      raise Exception('No real root found')
    x1 = (math.sqrt(D) - 2*(h*b**2 + m*k*a**2)) / (2*(b**2 + (m*a)**2))
    x2 = -(math.sqrt(D) + 2*(h*b**2 + m*k*a**2)) / (2*(b**2 + (m*a)**2))
    y1, y2 = m * x1, m * x2
  
    if abs(x1) < 1e-10 and abs(y1) < 1e-10:
      return x2 + h, y2 + k
    elif abs(x2) < 1e-10 and abs(y2) < 1e-10:
      return x1 + h, y1 + k
  
    x, y = x1, y1
    if θ < 180:
      x, y = (x1, y1) if y1 >= y2 else (x2, y2)
    elif θ == 180:
      x, y = x2, y2
    else:
      x, y = (x2, y2) if y1 >= y2 else (x1, y1)
    return x + h, y + k

  
  def find_intersection_after_reflection(self, a, b, x, y, θ):
    m = math.tan(math.radians(θ))
    slope = (m * y**2 * a**4 - m * x**2 * b**4 + 2 * x * b**2 * y * a**2) / \
            (    x**2 * b**4 -     y**2 * a**4 + 2 * x * b**2 * y * a**2 * m)
    θ = math.degrees(math.atan(slope))
    θ = 360 + θ if θ < 0 else θ
    x, y = self.find_intersection(a, b, x, y, θ)
    return θ, x, y


  def construct(self):
    global θ
    global origin

    x, y = origin
    if (x**2 / a**2 + y**2 / b**2) >= 1:
      print('Select a point within the ellipse')
      sys.exit(0)

    ellipse = Ellipse(width=2 * a, height=2 * b, color=BLUE, stroke_width=1)
    self.add(ellipse)

    point = Dot([*origin, 0], radius=0.04, color=RED)  
    path = TracedPath(point.get_center, stroke_width=0.6, dissipating_time=0.4, stroke_opacity=[0, 1])
    self.add(point, path)

    message = Text(
      'After how many reflections does the focal ray align itself with the major axis ?',
      font_size=22
    ).next_to(ellipse, UP, buff=0.1)

    try:
      x, y = self.find_intersection(a, b, *origin, θ)
      self.play(point.animate.move_to([x, y, 0]), Write(message))

    except Exception as error:
      print(error)

    for _ in range(iteration):
      try:
        θ, x, y = self.find_intersection_after_reflection(a, b, x, y, θ)
        path = TracedPath(point.get_center, stroke_width=0.6, dissipating_time=0.4, stroke_opacity=[0, 1])
        self.add(path)
        self.play(point.animate.move_to([x, y, 0]))
        self.wait()
      except Exception as error:
        print(error)
import sys
import math


a, b = 4, 3.5
iteration = 10
focus_length = math.sqrt(a**2 - b**2)
origin, θ = (-focus_length, 0), 60


class EllipseRay(Scene):
  
  # ellipse: (x - h)**2 / a**2 + (y - k)**2 / b**2 = 1
  #    line: y = tan(θ) * x
  def find_intersection(self, a, b, h, k, θ):
    m = math.tan(math.radians(θ))
    D = 4*((h*b**2 + m*k*a**2)**2 - (b**2 + (m*a)**2) * ((h*b)**2 + (k*a)**2 - (a*b)**2))
    if D < 0:
      raise Exception('No real root found')
    x1 = (math.sqrt(D) - 2*(h*b**2 + m*k*a**2)) / (2*(b**2 + (m*a)**2))
    x2 = -(math.sqrt(D) + 2*(h*b**2 + m*k*a**2)) / (2*(b**2 + (m*a)**2))
    y1, y2 = m * x1, m * x2
  
    if abs(x1) < 1e-10 and abs(y1) < 1e-10:
      return x2 + h, y2 + k
    elif abs(x2) < 1e-10 and abs(y2) < 1e-10:
      return x1 + h, y1 + k
  
    x, y = x1, y1
    if θ < 180:
      x, y = (x1, y1) if y1 >= y2 else (x2, y2)
    elif θ == 180:
      x, y = x2, y2
    else:
      x, y = (x2, y2) if y1 >= y2 else (x1, y1)
    return x + h, y + k

  
  def find_intersection_after_reflection(self, a, b, x, y, θ):
    m = math.tan(math.radians(θ))
    slope = (m * y**2 * a**4 - m * x**2 * b**4 + 2 * x * b**2 * y * a**2) / \
            (    x**2 * b**4 -     y**2 * a**4 + 2 * x * b**2 * y * a**2 * m)
    θ = math.degrees(math.atan(slope))
    θ = 360 + θ if θ < 0 else θ
    x, y = self.find_intersection(a, b, x, y, θ)
    return θ, x, y


  def construct(self):
    global θ
    global origin

    x, y = origin
    if (x**2 / a**2 + y**2 / b**2) >= 1:
      print('Select a point within the ellipse')
      sys.exit(0)

    ellipse = Ellipse(width=2 * a, height=2 * b, color=BLUE, stroke_width=1)
    self.add(ellipse)

    point = Dot([*origin, 0], radius=0.04, color=RED)  
    path = TracedPath(point.get_center, stroke_width=0.6, dissipating_time=0.4, stroke_opacity=[0, 1])
    self.add(point, path)

    message = Text(
      'After how many reflections does the focal ray align itself with the major axis ?',
      font_size=22
    ).next_to(ellipse, UP, buff=0.1)

    try:
      x, y = self.find_intersection(a, b, *origin, θ)
      self.play(point.animate.move_to([x, y, 0]), Write(message))

    except Exception as error:
      print(error)

    for _ in range(iteration):
      try:
        θ, x, y = self.find_intersection_after_reflection(a, b, x, y, θ)
        path = TracedPath(point.get_center, stroke_width=0.6, dissipating_time=0.4, stroke_opacity=[0, 1])
        self.add(path)
        self.play(point.animate.move_to([x, y, 0]))
        self.wait()
      except Exception as error:
        print(error)