import numpy as np
from numpy import linspace
from scipy import interpolate
from math import pi, asin, sin


class RoPsiSpline:
    """
    Epi- and endocardium contour by the spline interpolation on each slices
    """

    def __init__(self):
        self._ro_list = []
        self._z_list = []
        self._psi_list = []
        self._ro_array_1d = []
        self._psi_array = []
        self._z_array_1d = []

        self._Zmax = 0.
        self._h = 0.
        self._gamma = 0.

        self._psi_interval_points = 100  # default interpolated points number

    def set_coordiantes(self, ro_list, z_list):
        """
        Set (ro, z) coordinates as the spline nodes to build the spline

        Parameters
        ----------
        ro_list : list

        z_list: list
        """
        self._ro_list = ro_list
        self._z_list = z_list

    def set_Zmax(self, Zmax):
        """
        Set max Z value for the current meridian

        Parameters
        ----------
        Zmax : float
        """
        self._Zmax = Zmax

    def set_h(self, h):
        """
        Set h value (common for all meridians)

        Parameters
        ----------
        h : float
        """
        self._h = h

    def set_gamma(self, gamma):
        """
        Set gamma to define an epicardium or endocardium wall

        Parameters
        ----------
        gamma : float
        """
        self._gamma = gamma

    def set_psi_intervals_points(self, points_num):
        """
        Set an interpolated points number

        Parameters
        ----------
        points_num : int
        """
        self._psi_interval_points = points_num

    def get_ro_array(self):
        """
        Get a numpy array with interpolated ro coordinates

        Returns
        ----------
        get_ro_array : numpy array
        """
        return self._ro_array_1d

    def get_z_array(self):
        """
        Get a numpy array with computed z coordinates

        Returns
        ----------
        get_z_array : numpy array
        """
        return self._z_array_1d

    def get_psi_array(self):
        """
        Get a numpy array with psi coordinates

        Returns
        ----------
        get_psi_array : numpy array
        """
        return self._psi_array

    def _compute_ro_psi_spline(self):
        psi_0 = 0.
        psi_1 = pi/2

        psi_array = linspace(psi_0, psi_1, self._psi_interval_points)

        self._tck = interpolate.splrep(self._psi_list, self._ro_list, s=0)  # for b-spline
        output_ro = interpolate.splev(psi_array, self._tck)

        self._interpolate = interpolate.splev

        self._ro_array_1d = output_ro
        self._psi_array = psi_array

    def _compute_psi(self):
        self._psi_list = []
        for z in self._z_list:
            arg = (self._Zmax - z) / (self._Zmax - self._h*self._gamma)
            # To prevent domain error:
            if arg > 1.0:
                arg = 1.0
            psi = asin(arg)
            self._psi_list.append(psi)

    def _compute_z(self):
        self._z_array_1d = (self._Zmax - (self._Zmax - self._h*self._gamma)*np.sin(self._psi_array))

    def compute(self):
        """
        Compute spline with the set ro and z coordinates
        """
        self._compute_psi()
        self._compute_ro_psi_spline()
        self._compute_z()


import numpy as np
from numpy import linspace
from scipy import interpolate
from math import pi, asin, sin


class RoPsiSpline:
    """
    Epi- and endocardium contour by the spline interpolation on each slices
    """

    def __init__(self):
        self._ro_list = []
        self._z_list = []
        self._psi_list = []
        self._ro_array_1d = []
        self._psi_array = []
        self._z_array_1d = []

        self._Zmax = 0.
        self._h = 0.
        self._gamma = 0.

        self._psi_interval_points = 100  # default interpolated points number

    def set_coordiantes(self, ro_list, z_list):
        """
        Set (ro, z) coordinates as the spline nodes to build the spline

        Parameters
        ----------
        ro_list : list

        z_list: list
        """
        self._ro_list = ro_list
        self._z_list = z_list

    def set_Zmax(self, Zmax):
        """
        Set max Z value for the current meridian

        Parameters
        ----------
        Zmax : float
        """
        self._Zmax = Zmax

    def set_h(self, h):
        """
        Set h value (common for all meridians)

        Parameters
        ----------
        h : float
        """
        self._h = h

    def set_gamma(self, gamma):
        """
        Set gamma to define an epicardium or endocardium wall

        Parameters
        ----------
        gamma : float
        """
        self._gamma = gamma

    def set_psi_intervals_points(self, points_num):
        """
        Set an interpolated points number

        Parameters
        ----------
        points_num : int
        """
        self._psi_interval_points = points_num

    def get_ro_array(self):
        """
        Get a numpy array with interpolated ro coordinates

        Returns
        ----------
        get_ro_array : numpy array
        """
        return self._ro_array_1d

    def get_z_array(self):
        """
        Get a numpy array with computed z coordinates

        Returns
        ----------
        get_z_array : numpy array
        """
        return self._z_array_1d

    def get_psi_array(self):
        """
        Get a numpy array with psi coordinates

        Returns
        ----------
        get_psi_array : numpy array
        """
        return self._psi_array

    def _compute_ro_psi_spline(self):
        psi_0 = 0.
        psi_1 = pi/2

        psi_array = linspace(psi_0, psi_1, self._psi_interval_points)

        self._tck = interpolate.splrep(self._psi_list, self._ro_list, s=0)  # for b-spline
        output_ro = interpolate.splev(psi_array, self._tck)

        self._interpolate = interpolate.splev

        self._ro_array_1d = output_ro
        self._psi_array = psi_array

    def _compute_psi(self):
        self._psi_list = []
        for z in self._z_list:
            arg = (self._Zmax - z) / (self._Zmax - self._h*self._gamma)
            # To prevent domain error:
            if arg > 1.0:
                arg = 1.0
            psi = asin(arg)
            self._psi_list.append(psi)

    def _compute_z(self):
        self._z_array_1d = (self._Zmax - (self._Zmax - self._h*self._gamma)*np.sin(self._psi_array))

    def compute(self):
        """
        Compute spline with the set ro and z coordinates
        """
        self._compute_psi()
        self._compute_ro_psi_spline()
        self._compute_z()


Epi- and endocardium contour by the spline interpolation on each slices # default interpolated points number Set (ro, z) coordinates as the spline nodes to build the spline          Parameters         ----------         ro_list : list          z_list: list Set max Z value for the current meridian          Parameters         ----------         Zmax : float Set h value (common for all meridians)          Parameters         ----------         h : float Set gamma to define an epicardium or endocardium wall          Parameters         ----------         gamma : float Set an interpolated points number          Parameters         ----------         points_num : int Get a numpy array with interpolated ro coordinates          Returns         ----------         get_ro_array : numpy array Get a numpy array with computed z coordinates          Returns         ----------         get_z_array : numpy array Get a numpy array with psi coordinates          Returns         ----------         get_psi_array : numpy array # for b-spline # To prevent domain error: Compute spline with the set ro and z coordinates