
# paramUtils.py
# Andrew Davison, ad@coe.psu.ac.th, Oct. 2025


import math, statistics
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

import warnings
warnings.filterwarnings('ignore')
  # for use of 0's in setAxesEqual()


def parseEqus(fnm):
  # Parse the equations and t range, ignoring comments
  xEqu = None; yEqu = None; zEqu = None
  tMin = -10  # Default values
  tMax = 10
  step = 0.1
  title = 'Parameterized Curve Plot'
  a = 1; b = 1
  with open(fnm, 'r') as file:
    lines = file.readlines()
    for line in lines:
      line = line.strip()
      if line.startswith('#') or not line:
        continue
  
      if line.startswith('x ='):
        xEqu = line.split('=')[1].strip()
      elif line.startswith('y ='):
        yEqu = line.split('=')[1].strip()
      elif line.startswith('z ='):
        zEqu = line.split('=')[1].strip()
  
      elif line.startswith('tMin ='):
        tMin = float(line.split('=')[1].strip())
      elif line.startswith('tMax ='):
        tMax = float(line.split('=')[1].strip())
      elif line.startswith('step ='):
        step = float(line.split('=')[1].strip())
  
      elif line.startswith('a ='):
        a = float(line.split('=')[1].strip())
      elif line.startswith('b ='):
        b = float(line.split('=')[1].strip())

      elif line.startswith('title ='):
        title = line.split('=')[1].strip()

  if not xEqu or not yEqu:
      print("Error: Equations for x and y not found.")
      sys.exit(1)
  
  # Generate t values based on the parsed range and step
  if tMin >= tMax or step <= 0:
      print("Error: Invalid t range or step size.")
      sys.exit(1)
  
  ts = [tMin + i * step for i in range(int((tMax - tMin) / step) + 1)]
  return title, a, b, xEqu, yEqu, zEqu, ts


def evalEqu(equ, a, b, ts):
  try:
    return [eval(equ, {'t': t, 'a': a, 'b': b, 'math': math}) for t in ts]
  except Exception as e:
    print("Eval error:", e)
    return []


def addSliders(a, b):
  aBar = plt.axes([0.25, 0.1, 0.65, 0.03])  
  bBar = plt.axes([0.25, 0.05, 0.65, 0.03])
  aSlider = Slider(aBar, 'a', -5.0, 5.0, valinit=a, valstep=0.01)
  bSlider = Slider(bBar, 'b', -5.0, 5.0, valinit=b, valstep=0.01)
  return aSlider, bSlider


def setAxesEqual(ax):
  """
  Make axes of 3D plot have equal scale so that spheres 
  appear as spheres, cubes as cubes, etc.
  """
  xlims = ax.get_xlim3d()
  ylims = ax.get_ylim3d()
  zlms = ax.get_zlim3d()

  xRange = abs(xlims[1] - xlims[0])
  xMid = statistics.mean(xlims)
  yRange = abs(ylims[1] - ylims[0])
  yMid = statistics.mean(ylims)
  zRange = abs(zlms[1] - zlms[0])
  zMid = statistics.mean(zlms)

  # The plot bounding box is a sphere in the sense of the infinity
  # norm, hence I call half the max range the plot radius.
  r = 0.5*max([xRange, yRange, zRange])

  ax.set_xlim3d([xMid - r, xMid + r])
  ax.set_ylim3d([yMid - r, yMid + r])
  ax.set_zlim3d([zMid - r, zMid + r])

