def _get_emptiest_area(ax, factor, target_x, areas_to_avoid=[]): """ Get's the emptiest area of size (1/factor x 1/factor) compared to the overall size of the plot area. ax - the axes of the plot factor - 1 / the fraction of the size the area should be target_x - the ideal x-value for the area to be centred on areas_to_avoid - a list of figure-space Rectangles which must be avoided returns a Rectangle in figure-space which """ lines = ax.get_lines() min_points = np.inf min_rect = None x_range = ax.get_xlim() coord_width = x_range[1] - x_range[0] plot_width = coord_width / factor y_range = ax.get_ylim() coord_height = y_range[1] - y_range[0] plot_height = coord_height / factor # Change the target x so that the centre will be at the target x target_x -= plot_width / 2 if target_x < x_range[0]: target_x = x_range[0] # Start from the target x as an ideal position, then go right, then left for i in np.concatenate([np.linspace(target_x, x_range[1] - plot_width, 10), np.linspace(target_x, x_range[0], 10)]): # Start from the TOP of the plot as ideal, then downwards for j in np.linspace(y_range[1] - plot_height, y_range[0], 10): rect = Rectangle([i, j], plot_width, plot_height) overlap = False # Check that this rectangle will not overlap any of the explicitly-banned areas rect_bbox = _coord_space_rect_to_figure_space_rect(rect, ax).get_bbox() for area in areas_to_avoid: if rect_bbox.overlaps(area.get_bbox()): overlap = True break if overlap: continue points = 0 for line in lines: for point in line.get_xydata(): if rect.contains_point(point, radius=0.0): points += 1 if points < min_points: min_points = points min_rect = rect if min_points == 0: break if min_points == 0: break return _coord_space_rect_to_figure_space_rect(min_rect, ax)