# Path: batoid/utils.py
# def bilinear_fit(ux, uy, kx, ky):
#     a = np.empty((len(ux), 3), dtype=float)
#     a[:,0] = 1
#     a[:,1] = ux
#     a[:,2] = uy
#     b = np.empty((len(ux), 2), dtype=float)
#     b[:,0] = kx
#     b[:,1] = ky
#     x, _, _, _ = np.linalg.lstsq(a, b, rcond=-1)
#     return x
# 
# def fieldToDirCos(u, v, projection='postel'):
#     """Convert field angle to direction cosines using specified projection.
# 
#     Parameters
#     ----------
#     u, v : float
#         Tangent plane coordinates in radians.
#     projection : {'postel', 'zemax', 'gnomonic', 'stereographic', 'lambert', 'orthographic'}
#         Projection used to convert field angle to direction cosines.
# 
#     Returns
#     -------
#     alpha, beta, gamma : float
#         Direction cosines (unit vector projected onto x, y, z in order)
# 
#     Notes
#     -----
#     The tangent plane reference is at (u,v) = (0,0), which corresponds to
#     (alpha, beta, gamma) = (0, 0, -1) (a ray coming directly from above).  The
#     orientation is such that vx (vy) is positive when u (v) is positive.
#     """
#     if projection == 'postel':
#         return postelToDirCos(u, v)
#     elif projection == 'zemax':
#         return zemaxToDirCos(u, v)
#     elif projection == 'gnomonic':
#         return gnomonicToDirCos(u, v)
#     elif projection == 'stereographic':
#         return stereographicToDirCos(u, v)
#     elif projection == 'lambert':
#         return lambertToDirCos(u, v)
#     elif projection == 'orthographic':
#         return orthographicToDirCos(u, v)
#     else:
#         raise ValueError("Bad projection: {}".format(projection))


import numpy as np
import batoid
    import galsim
    import galsim
    import galsim
    import galsim
from .utils import bilinear_fit, fieldToDirCos
    from numbers import Real
        If 'chief', then center the output lattice where the chief ray
        intersects the focal plane.  If 'mean', then center at the mean
        non-vignetted ray intersection.
    jmax : int, optional
        Number of coefficients to compute.  Default: 12.
    eps : float, optional
        Use annular Zernike polynomials with this fractional inner radius.
        Default: 0.0.

    Returns
    -------
    zernikes : array
        Zernike polynomial coefficients in waves.

    Notes
    -----
    Zernike coefficients are indexed following the Noll convention.
    Additionally, since python lists start at 0, but the Noll convention starts
    at 1, the 0-th index of the returned array is meaningless.  I.e.,
    zernikes[1] is piston, zernikes[4] is defocus, and so on...

    Also, since Zernike polynomials are orthogonal over a circle or annulus,
    but vignetting may make the actual wavefront region of support something
    different, the values of fit coefficients can depend on the total number of
    coefficients being fit.  For example, the j=4 (defocus) coefficient may
    depend on whether jmax=11 and jmax=21 (or some other value).  See the
    zernikeGQ function for an alternative algorithm that is independent of
    jmax.
    """


# Path: batoid/utils.py
# def bilinear_fit(ux, uy, kx, ky):
#     a = np.empty((len(ux), 3), dtype=float)
#     a[:,0] = 1
#     a[:,1] = ux
#     a[:,2] = uy
#     b = np.empty((len(ux), 2), dtype=float)
#     b[:,0] = kx
#     b[:,1] = ky
#     x, _, _, _ = np.linalg.lstsq(a, b, rcond=-1)
#     return x
# 
# def fieldToDirCos(u, v, projection='postel'):
#     """Convert field angle to direction cosines using specified projection.
# 
#     Parameters
#     ----------
#     u, v : float
#         Tangent plane coordinates in radians.
#     projection : {'postel', 'zemax', 'gnomonic', 'stereographic', 'lambert', 'orthographic'}
#         Projection used to convert field angle to direction cosines.
# 
#     Returns
#     -------
#     alpha, beta, gamma : float
#         Direction cosines (unit vector projected onto x, y, z in order)
# 
#     Notes
#     -----
#     The tangent plane reference is at (u,v) = (0,0), which corresponds to
#     (alpha, beta, gamma) = (0, 0, -1) (a ray coming directly from above).  The
#     orientation is such that vx (vy) is positive when u (v) is positive.
#     """
#     if projection == 'postel':
#         return postelToDirCos(u, v)
#     elif projection == 'zemax':
#         return zemaxToDirCos(u, v)
#     elif projection == 'gnomonic':
#         return gnomonicToDirCos(u, v)
#     elif projection == 'stereographic':
#         return stereographicToDirCos(u, v)
#     elif projection == 'lambert':
#         return lambertToDirCos(u, v)
#     elif projection == 'orthographic':
#         return orthographicToDirCos(u, v)
#     else:
#         raise ValueError("Bad projection: {}".format(projection))


# Path: batoid/analysis.py
import numpy as np
import batoid
    import galsim
    import galsim
    import galsim
    import galsim
from .utils import bilinear_fit, fieldToDirCos
    from numbers import Real

        If 'chief', then center the output lattice where the chief ray
        intersects the focal plane.  If 'mean', then center at the mean
        non-vignetted ray intersection.
    jmax : int, optional
        Number of coefficients to compute.  Default: 12.
    eps : float, optional
        Use annular Zernike polynomials with this fractional inner radius.
        Default: 0.0.

    Returns
    -------
    zernikes : array
        Zernike polynomial coefficients in waves.

    Notes
    -----
    Zernike coefficients are indexed following the Noll convention.
    Additionally, since python lists start at 0, but the Noll convention starts
    at 1, the 0-th index of the returned array is meaningless.  I.e.,
    zernikes[1] is piston, zernikes[4] is defocus, and so on...

    Also, since Zernike polynomials are orthogonal over a circle or annulus,
    but vignetting may make the actual wavefront region of support something
    different, the values of fit coefficients can depend on the total number of
    coefficients being fit.  For example, the j=4 (defocus) coefficient may
    depend on whether jmax=11 and jmax=21 (or some other value).  See the
    zernikeGQ function for an alternative algorithm that is independent of
    jmax.
    """


    dirCos = fieldToDirCos(theta_x, theta_y, projection=projection)