# Source code for SpindleAssembly.PositionIpMTs

    from SpindleAssembly import PositionComponents
    import numpy as np
    import math

    def generate_separation(**kwargs):
        """
        Calculate distance and angle between ipMTs based on the Gaussian distribution with a given mean and standard
        deviation

        :param kwargs: model parameters

        :type kwargs: object

        :return: s: distance between ipMTs

        :rtype: s: float

        :return: alpha: angle between ipMTs in degrees

        :rtype: float
        """
        s = np.random.normal(loc=kwargs['separation'][0], scale=kwargs['separation'][1])
        # Generate angle between MTs
        alpha = np.random.normal(loc=kwargs['angle'][0], scale=kwargs['angle'][1])

        return s, alpha

    def check_MT_within_centrosome(pos, **kwargs):
        """
        Add ipMT if its x and y coordinates do not exceed the radius of the
        circular cross-section of a centrosome

        :param pos: (x, y) -> local coordinates of ipMT

        :param kwargs: object

        :return: True or False
        """
        centrosomeRadius = kwargs['CentrosomeRadius']
        x = pos[0]
        y = pos[1]
        if not -centrosomeRadius <= x <= centrosomeRadius \
                and -centrosomeRadius <= y <= centrosomeRadius:
            return False
        return True

    def condition_for_fourth_ipMT( alpha2, alpha3, s, pos3 ):
        """
        Condition of positioning the fourth ipMT

        :param alpha2: Angle between ipMTs number 2 and 3

        :type alpha2: float

        :param alpha3: Angle between ipMTs number 3 and 4

        :type alpha3: float

        :param s: Separation between ipMTs

        :type s: float

        :param pos3: (x, y) Position of the third ipMT

        :type pos3: tuple

        :return: y coordinate of the ipMT, mewalpha angle of the ipMT

        :rtype: float, float
        """
        if math.radians(alpha3) >= math.pi - math.radians(alpha2):
            newalpha = math.radians(alpha3) - (math.pi - math.radians(alpha2))
            y = pos3[1] + s * math.sin(newalpha)
        else:
            newalpha = - math.radians(alpha3) + (math.pi - math.radians(alpha2))
            y = pos3[1] - s * math.sin(newalpha)
        return y, newalpha

    def add_first_ipMT( i, **kwargs ):
        """
        Add and position the first ipMT within the spindle

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        x = -0.018
        y = -0.018
        pos = (x, y)
        kwargs['x'] = x
        kwargs['y'] = y
        kwargs['index'] = 1

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_right(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        alpha = 0
        return MTname, pos, alpha, kwargs

    def add_second_ipMT( pos, alpha1, i, **kwargs ):
        """
        Add and position the second ipMT within the spindle

        :param pos: (x, y) position of the previously added ipMT

        :type pos: tuple

        :param alpha1: Angle of the previously added ipMT

        :type alpha1: float

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        # Generate position of the ipMT
        s, alpha = generate_separation(**kwargs)
        x = pos[0] + s * math.cos(math.radians(alpha1))
        y = pos[1] + s * math.sin(math.radians(alpha1))
        pos = (x, y)

        # Correct the position until it is within centrosome
        counter = 0
        while not check_MT_within_centrosome(pos, **kwargs):
            if counter > 1000:
                raise ValueError('Some MTs are located outside of the centrosomes. Consider restarting calculation')
                break
            s, alpha = generate_separation(**kwargs)
            # Position MT
            x = pos[0] + s * math.cos(math.radians(alpha1))
            y = pos[1] + s * math.sin(math.radians(alpha1))
            kwargs['x'] = x
            kwargs['y'] = y
            pos = (x, y)
            counter += 1

        # Add and position ipMT
        kwargs['index'] = 2

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_left(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        return MTname, pos, alpha, kwargs

    def add_third_ipMT( pos2, alpha2, i, **kwargs ):
        """
        Add and position the third ipMT within the spindle

        :param pos2: (x, y) position of the previously added ipMT

        :type pos2: tuple

        :param alpha2: Angle of the previously added ipMT

        :type alpha2: float

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        # Generate position of the ipMT
        s, alpha = generate_separation(**kwargs)
        x = pos2[0] - s * math.cos(math.radians(alpha2))
        y = pos2[1] + s * math.sin(math.radians(alpha2))
        pos = (x, y)

        # Correct the position until it is within centrosome
        counter = 0
        while not check_MT_within_centrosome(pos, **kwargs):
            if counter > 1000:
                raise ValueError('Some MTs are located outside of the centrosomes. Consider restarting calculation')
                break
            x = pos2[0] - s * math.cos(math.radians(alpha2))
            y = pos2[1] + s * math.sin(math.radians(alpha2))
            kwargs['x'] = x
            kwargs['y'] = y
            counter += 1

        # Add and position ipMT
        kwargs['index'] = 3

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_right(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        return MTname, pos, alpha, kwargs

    def add_fourth_ipMT( pos3, alpha2, alpha3, i, **kwargs ):
        """
        Add and position the fourth ipMT within the spindle

        :param pos3: (x, y) position of the previously added ipMT

        :type pos3: tuple

        :param alpha2: Angle of ipMT 2

        :type alpha2: float

        :param alpha3: Angle of ipMT 3

        :type alpha3: float

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        # Generate position of the ipMT
        s, alpha = generate_separation(**kwargs)
        y, newalpha = condition_for_fourth_ipMT(alpha2, alpha3, s, pos3)
        x = pos3[0] - s * math.cos(newalpha)
        pos = (x, y)

        # Correct the position until it is within centrosome
        counter = 0
        while not check_MT_within_centrosome(pos, **kwargs):
            if counter > 1000:
                raise ValueError('Some MTs are located outside of the centrosomes. Consider restarting calculation')
                break
            y, newalpha = condition_for_fourth_ipMT(alpha2, alpha3, s, pos3)
            x = pos3[0] - s * math.cos(newalpha)
            kwargs['x'] = x
            kwargs['y'] = y
            counter += 1

        # Add and position ipMT
        kwargs['index'] = 4

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_left(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        return MTname, pos, newalpha, kwargs

    def add_fifth_ipMT( pos4, alpha4, i, **kwargs ):
        """
        Add and position the fifth ipMT within the spindle

        :param pos4: (x, y) position of the previously added ipMT

        :type pos4: tuple

        :param alpha4: Angle of the previously added ipMT

        :type alpha4: float

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        # Generate position of the ipMT
        s, alpha = generate_separation(**kwargs)
        x = pos4[0] - s * math.cos(-math.radians(alpha4))
        y = pos4[1] + s * math.sin(-math.radians(alpha4))
        pos = (x, y)

        # Correct the position until it is within centrosome
        counter = 0
        while not check_MT_within_centrosome(pos, **kwargs):

            if counter > 1000:
                raise ValueError('Some MTs are located outside of the centrosomes. Consider restarting calculation')
                break

            x = pos4[0] - s * math.cos(-math.radians(alpha4))
            y = pos4[1] + s * math.sin(-math.radians(alpha4))
            kwargs['x'] = x
            kwargs['y'] = y
            counter += 1

        # Add and position ipMT
        kwargs['index'] = 5

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_right(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        return MTname, pos, alpha, kwargs

    def add_sixth_ipMT( pos5, alpha4, alpha5, i, **kwargs ):
        """
        Add and position sixth ipMT within the spindle

        :param pos5: (x, y) position of the previously added ipMT

        :type pos5: tuple

        :param alpha4: Angle of the ipMT 4

        :type alpha4: float

        :param alpha5: Angle of the ipMT 5

        :type alpha5: float

        :param i: Sequential number of ipMT

        :type i: int

        :param kwargs: model parameters

        :type kwargs: dict

        :return: MTname, pos, alpha, kwargs

        :rtype: str, tuple, float, dict
        """
        # Generate position of the ipMT
        s, alpha = generate_separation(**kwargs)
        x = pos5[0] + s * math.cos(-math.radians(alpha5) + math.radians(alpha4))
        y = pos5[1] + s * math.sin(-math.radians(alpha5) + math.radians(alpha4))
        pos = (x, y)

        # Correct the position until it is within centrosome
        counter = 0
        while not check_MT_within_centrosome(pos, **kwargs):
            if counter > 1000:
                raise ValueError('Some MTs are located outside of the centrosomes. Consider restarting calculation')
                break
            x = pos5[0] + s * math.cos(-math.radians(alpha5) + math.radians(alpha4))
            y = pos5[1] + s * math.sin(-math.radians(alpha5) + math.radians(alpha4))
            kwargs['x'] = x
            kwargs['y'] = y
            counter += 1

        # Add and position ipMT
        kwargs['index'] = 6

        MTname, zBeginPos, zEndPos, l = PositionComponents.assign_ipMTs_left(pos, i, **kwargs)
        pos = (x, y, zBeginPos, zEndPos, l)

        return MTname, pos, alpha, kwargs