def adjust_text(x, y, texts, ax=None, expand_text=(1.2, 1.2),
                expand_points=(1.2, 1.2), autoalign=True,  va='center',
                ha='center', force_text=1., force_points=1.,
                lim=100, precision=0, only_move={}, text_from_text=True,
                text_from_points=True, save_steps=False, save_prefix='',
                save_format='png', add_step_numbers=True, draggable=True, repel_from_axes=False, min_arrow_sep=0.0,
                *args, **kwargs):
    """
    Iteratively adjusts the locations of texts. First moves all texts that are
    outside the axes limits inside. Then in each iteration moves all texts away
    from each other and from points. In the end hides texts and substitutes
    them with annotations to link them to the rescpective points.

    Args:
        x (seq): x-coordinates of labelled points
        y (seq): y-coordinates of labelled points
        texts (list): a list of text.Text objects to adjust
        ax (obj): axes object with the plot; if not provided is determined by
            plt.gca()
        expand_text (seq): a tuple/list/... with 2 numbers (x, y) to expand
            texts when repelling them from each other; default (1.2, 1.2)
        expand_points (seq): a tuple/list/... with 2 numbers (x, y) to expand
            texts when repelling them from points; default (1.2, 1.2)
        autoalign (bool): If True, the best alignment of all texts will be
            determined automatically before running the iterative adjustment;
            if 'x' will only align horizontally, if 'y' - vertically; overrides
            va and ha
        va (str): vertical alignment of texts
        ha (str): horizontal alignment of texts
        force_text (float): the repel force from texts is multiplied by this
            value; default 0.5
        force_points (float): the repel force from points is multiplied by this
            value; default 0.5
        lim (int): limit of number of iterations
        precision (float): up to which sum of all overlaps along both x and y
            to iterate; may need to increase for complicated situations;
            default 0, so no overlaps with anything.
        only_move (dict): a dict to restrict movement of texts to only certain
            axis. Valid keys are 'points' and 'text', for each of them valid
            values are 'x', 'y' and 'xy'. This way you can forbid moving texts
            along either of the axes due to overlaps with points, but let it
            happen if there is an overlap with texts: only_move={'points':'y',
            'text':'xy'}. Default: None, so everything is allowed.
        text_from_text (bool): whether to repel texts from each other; default
            True
        text_from_points (bool): whether to repel texts from points; default
            True; can helpful to switch of in extremely crouded plots
        save_steps (bool): whether to save intermediate steps as images;
            default False
        save_prefix (str): a path and/or prefix to the saved steps; default ''
        save_format (str): a format to save the steps into; default 'png
        *args and **kwargs: any arguments will be fed into plt.annotate after
            all the optimization is done just for plotting
        add_step_numbers (bool): whether to add step numbers as titles to the
            images of saving steps
        draggable (bool): whether to make the annotations draggable; default
            True
    """
    if ax is None:
        ax = plt.gca()
        r = ax.get_figure().canvas.get_renderer()
    orig_xy = [text.get_position() for text in texts]
    orig_x = [xy[0] for xy in orig_xy]
    orig_y = [xy[1] for xy in orig_xy]
    for text in texts:
        text.set_va(va)
        text.set_ha(ha)
    if save_steps:
        if add_step_numbers:
            plt.title('0a')
        plt.savefig(save_prefix + '0a.' + save_format, format=save_format)
    if autoalign:
        if autoalign is not True:
            texts = optimally_align_text(x, y, texts,
                                         direction=autoalign,
                                         expand=expand_points, renderer=r,
                                         ax=ax)
        else:
            texts = optimally_align_text(orig_x, orig_y, texts,
                                         expand=expand_points, renderer=r,
                                         ax=ax)

    if save_steps:
        if add_step_numbers:
            plt.title('0b')
        plt.savefig(save_prefix + '0b.' + save_format, format=save_format)
    if repel_from_axes is True:
        texts = repel_text_from_axes(
            texts, ax, renderer=r, expand=expand_points)
    history = [np.inf] * 5
    for i in xrange(lim):
        q1, q2 = np.inf, np.inf
        if text_from_text:
            d_x_text, d_y_text, q1 = repel_text(texts, renderer=r, ax=ax,
                                                expand=expand_text)
        else:
            d_x_text, d_y_text, q1 = [0] * len(texts), [0] * len(texts), 0
        if text_from_points:
            d_x_points, d_y_points, q2 = repel_text_from_points(x, y, texts,
                                                                ax=ax, renderer=r,
                                                                expand=expand_points)
        else:
            d_x_points, d_y_points, q1 = [0] * len(texts), [0] * len(texts), 0
        if only_move:
            if 'text' in only_move:
                if 'x' not in only_move['text']:
                    d_x_text = np.zeros_like(d_x_text)
                if 'y' not in only_move['text']:
                    d_y_text = np.zeros_like(d_y_text)
            if 'points' in only_move:
                if 'x' not in only_move['points']:
                    d_x_points = np.zeros_like(d_x_points)
                if 'y' not in only_move['points']:
                    d_y_points = np.zeros_like(d_y_points)
        dx = np.array(d_x_text) + np.array(d_x_points)
        dy = np.array(d_y_text) + np.array(d_y_points)
        q = round(np.sum(np.array([q1, q2])[np.array([q1, q2]) < np.inf]), 5)
        if q > precision and q < np.max(history):
            history.pop(0)
            history.append(q)
            move_texts(texts, dx * force_text, dy * force_points,
                       bboxes=get_bboxes(texts, r, (1, 1)), ax=ax)
            if save_steps:
                if add_step_numbers:
                    plt.title(i + 1)
                    plt.savefig(save_prefix + str(i + 1) + '.' + save_format,
                                format=save_format)
        else:
            break

    bboxes = get_bboxes(texts, r, (1, 1))
    originLW = kwargs["arrowprops"]["lw"]
    for j, text in enumerate(texts):
        cx, cy = get_midpoint(bboxes[j])
        one = (orig_xy[j][0] - cx)**2
        two = (orig_xy[j][1] - cy)**2
        sep = (one + two)**0.5

        print text.get_text(), sep
        try:
            if sep < min_arrow_sep:
                kwargs["arrowprops"]["lw"] = 0.
            else:
                kwargs["arrowprops"]["lw"] = originLW
        except Exception, e:
            print e
        a = ax.annotate(text.get_text(), xy=(orig_xy[j]),
                        xytext=text.get_position(), *args, **kwargs)
        a.__dict__.update(text.__dict__)
        if draggable:
            a.draggable()
        texts[j].remove()
    if save_steps:
        if add_step_numbers:
            plt.title(i + 1)
        plt.savefig(save_prefix + str(i + 1) + '.' +
                    save_format, format=save_format)