Joins other to self at the C-terminus via a peptide bond.

        Notes
        -----
        This function directly modifies self. It does not return a new object.

        Parameters
        ----------
        other: Residue or Polypeptide
        psi: float, optional
            Psi torsion angle (degrees) between final `Residue` of self
            and first `Residue` of other.
        omega: float, optional
            Omega torsion angle (degrees) between final `Residue` of
            self and first `Residue` of other.
        phi: float, optional
            Phi torsion angle (degrees) between final `Residue` of self
            and first `Residue` of other.
        o_c_n_angle: float or None, optional
            Desired angle between O, C (final `Residue` of self) and N
            (first `Residue` of other) atoms. If `None`, default value is
            taken from `ideal_backbone_bond_angles`.
        c_n_ca_angle: float or None, optional
            Desired angle between C (final `Residue` of self) and N, CA
            (first `Residue` of other) atoms. If `None`, default value is
            taken from `ideal_backbone_bond_angles`.
        c_n_length: float or None, optional
            Desired peptide bond length between final `Residue` of self
            and first `Residue` of other. If `None`, default value is taken
            from `ideal_backbone_bond_lengths`.
        relabel: bool, optional
            If `True`, `relabel_all` is run on self before returning.

        Raises
        ------
        TypeError:
            If other is not a `Residue` or a Polypeptide.

def c_join(self, other, psi=-40.76, omega=-178.25, phi=-65.07,
               o_c_n_angle=None, c_n_ca_angle=None, c_n_length=None,
               relabel=True):
        """Joins other to self at the C-terminus via a peptide bond.

        Notes
        -----
        This function directly modifies self. It does not return a new object.

        Parameters
        ----------
        other: Residue or Polypeptide
        psi: float, optional
            Psi torsion angle (degrees) between final `Residue` of self
            and first `Residue` of other.
        omega: float, optional
            Omega torsion angle (degrees) between final `Residue` of
            self and first `Residue` of other.
        phi: float, optional
            Phi torsion angle (degrees) between final `Residue` of self
            and first `Residue` of other.
        o_c_n_angle: float or None, optional
            Desired angle between O, C (final `Residue` of self) and N
            (first `Residue` of other) atoms. If `None`, default value is
            taken from `ideal_backbone_bond_angles`.
        c_n_ca_angle: float or None, optional
            Desired angle between C (final `Residue` of self) and N, CA
            (first `Residue` of other) atoms. If `None`, default value is
            taken from `ideal_backbone_bond_angles`.
        c_n_length: float or None, optional
            Desired peptide bond length between final `Residue` of self
            and first `Residue` of other. If `None`, default value is taken
            from `ideal_backbone_bond_lengths`.
        relabel: bool, optional
            If `True`, `relabel_all` is run on self before returning.

        Raises
        ------
        TypeError:
            If other is not a `Residue` or a Polypeptide.
        """
        if isinstance(other, Residue):
            other = Polypeptide([other])
        if not isinstance(other, Polypeptide):
            raise TypeError(
                'Only Polypeptide or Residue objects can be joined to a Polypeptide')
        if abs(omega) >= 90:
            peptide_conformation = 'trans'
        else:
            peptide_conformation = 'cis'
        if o_c_n_angle is None:
            o_c_n_angle = ideal_backbone_bond_angles[peptide_conformation]['o_c_n']
        if c_n_ca_angle is None:
            c_n_ca_angle = ideal_backbone_bond_angles[peptide_conformation]['c_n_ca']
        if c_n_length is None:
            c_n_length = ideal_backbone_bond_lengths['c_n']
        r1 = self[-1]
        r1_ca = r1['CA']._vector
        r1_c = r1['C']._vector
        r1_o = r1['O']._vector
        # p1 is point that will be used to position the N atom of r2.
        p1 = r1_o[:]
        # rotate p1 by o_c_n_angle, about axis perpendicular to the
        # r1_ca, r1_c, r1_o plane, passing through r1_c.
        axis = numpy.cross((r1_ca - r1_c), (r1_o - r1_c))
        q = Quaternion.angle_and_axis(angle=o_c_n_angle, axis=axis)
        p1 = q.rotate_vector(v=p1, point=r1_c)
        # Ensure p1 is separated from r1_c by the correct distance.
        p1 = r1_c + (c_n_length * unit_vector(p1 - r1_c))
        # rotate p1 and r1['O'] by to obtain desired psi value at the join.
        measured_psi = dihedral(r1['N'], r1['CA'], r1['C'], p1)
        q = Quaternion.angle_and_axis(
            angle=(psi - measured_psi), axis=(r1_c - r1_ca))
        p1 = q.rotate_vector(v=p1, point=r1_c)
        r1['O']._vector = q.rotate_vector(v=r1_o, point=r1_c)
        # translate other so that its first N atom is at p1
        other.translate(vector=(p1 - other[0]['N']._vector))
        # rotate other so that c_n_ca angle is correct.
        v1 = r1_c - other[0]['N']._vector
        v2 = other[0]['CA']._vector - other[0]['N']._vector
        measured_c_n_ca = angle_between_vectors(v1, v2)
        axis = numpy.cross(v1, v2)
        other.rotate(angle=(c_n_ca_angle - measured_c_n_ca),
                     axis=axis, point=other[0]['N']._vector)
        # rotate other to obtain desired omega and phi values at the join
        measured_omega = dihedral(
            r1['CA'], r1['C'], other[0]['N'], other[0]['CA'])
        other.rotate(angle=(omega - measured_omega),
                     axis=(other[0]['N'] - r1['C']), point=other[0]['N']._vector)
        measured_phi = dihedral(
            r1['C'], other[0]['N'], other[0]['CA'], other[0]['C'])
        other.rotate(angle=(phi - measured_phi),
                     axis=(other[0]['CA'] - other[0]['N']), point=other[0]['CA']._vector)
        self.extend(other)
        if relabel:
            self.relabel_all()
        self.tags['assigned_ff'] = False
        return