summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Sotoudeh <matthewsot@outlook.com>2017-07-27 09:07:48 -0700
committerMatthew Sotoudeh <matthewsot@outlook.com>2017-07-27 09:07:48 -0700
commit498aa8d2ca7be1880e5df0c4e3e15ea9bdf60919 (patch)
tree0dca4e3a4f96a11dd624db6bc059126548da06d3
parent63744d59c4bf999a0ba7bd5b7f7080f830aedeb7 (diff)
Refactored the legend placement code into cbookauto-placement
-rw-r--r--lib/matplotlib/cbook/__init__.py93
-rw-r--r--lib/matplotlib/legend.py95
2 files changed, 95 insertions, 93 deletions
diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py
index 9a143cda4..6caa6ca48 100644
--- a/lib/matplotlib/cbook/__init__.py
+++ b/lib/matplotlib/cbook/__init__.py
@@ -2707,3 +2707,96 @@ class _StringFuncParser(object):
(params, str_func))
return str_func, params
+
+def _auto_legend_data(ax):
+ """
+ Returns list of vertices and extents covered by the plot.
+
+ Returns a two long list.
+
+ First element is a list of (x, y) vertices (in
+ display-coordinates) covered by all the lines and line
+ collections, in the legend's handles.
+
+ Second element is a list of bounding boxes for all the patches in
+ the legend's handles.
+ """
+
+ bboxes = []
+ lines = []
+ offsets = []
+
+ from matplotlib.lines import Line2D
+ from matplotlib.patches import Patch, Rectangle
+
+ for handle in ax.lines:
+ assert isinstance(handle, Line2D)
+ path = handle.get_path()
+ trans = handle.get_transform()
+ tpath = trans.transform_path(path)
+ lines.append(tpath)
+
+ for handle in ax.patches:
+ assert isinstance(handle, Patch)
+
+ if isinstance(handle, Rectangle):
+ transform = handle.get_data_transform()
+ bboxes.append(handle.get_bbox().transformed(transform))
+ else:
+ transform = handle.get_transform()
+ bboxes.append(handle.get_path().get_extents(transform))
+
+ for handle in ax.collections:
+ transform, transOffset, hoffsets, paths = handle._prepare_points()
+
+ if len(hoffsets):
+ for offset in transOffset.transform(hoffsets):
+ offsets.append(offset)
+
+ try:
+ vertices = np.concatenate([l.vertices for l in lines])
+ except ValueError:
+ vertices = np.array([])
+
+ return [vertices, bboxes, lines, offsets]
+
+def find_best_position(artist, width, height, renderer, consider=None):
+ """
+ Determine the best location to place the legend.
+
+ `consider` is a list of (x, y) pairs to consider as a potential
+ lower-left corner of the legend. All are display coords.
+ """
+ from matplotlib.transforms import Bbox
+
+ # should always hold because function is only called internally
+ assert artist.isaxes
+
+ verts, bboxes, lines, offsets = _auto_legend_data(artist.parent)
+
+ bbox = Bbox.from_bounds(0, 0, width, height)
+ if consider is None:
+ consider = [artist._get_anchored_bbox(x, bbox,
+ artist.get_bbox_to_anchor(),
+ renderer)
+ for x in range(1, len(artist.codes))]
+
+ candidates = []
+ for idx, (l, b) in enumerate(consider):
+ legendBox = Bbox.from_bounds(l, b, width, height)
+ badness = 0
+ # XXX TODO: If markers are present, it would be good to
+ # take them into account when checking vertex overlaps in
+ # the next line.
+ badness = (legendBox.count_contains(verts)
+ + legendBox.count_contains(offsets)
+ + legendBox.count_overlaps(bboxes)
+ + sum(line.intersects_bbox(legendBox, filled=False)
+ for line in lines))
+ if badness == 0:
+ return l, b
+ # Include the index to favor lower codes in case of a tie.
+ candidates.append((badness, idx, (l, b)))
+
+ _, _, (l, b) = min(candidates)
+ return l, b
diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py
index 2b99587e8..2ec62ae8f 100644
--- a/lib/matplotlib/legend.py
+++ b/lib/matplotlib/legend.py
@@ -34,7 +34,7 @@ import numpy as np
from matplotlib import rcParams
from matplotlib.artist import Artist, allow_rasterization
-from matplotlib.cbook import silent_list, is_hashable
+from matplotlib.cbook import silent_list, is_hashable, find_best_position
from matplotlib.font_manager import FontProperties
from matplotlib.lines import Line2D
from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch
@@ -421,7 +421,7 @@ class Legend(Artist):
"Helper function to locate the legend"
if self._loc == 0: # "best".
- x, y = self._find_best_position(width, height, renderer)
+ x, y = find_best_position(self, width, height, renderer)
elif self._loc in Legend.codes.values(): # Fixed location.
bbox = Bbox.from_bounds(0, 0, width, height)
x, y = self._get_anchored_bbox(self._loc, bbox,
@@ -691,58 +691,6 @@ class Legend(Artist):
self.texts = text_list
self.legendHandles = handle_list
- def _auto_legend_data(self):
- """
- Returns list of vertices and extents covered by the plot.
-
- Returns a two long list.
-
- First element is a list of (x, y) vertices (in
- display-coordinates) covered by all the lines and line
- collections, in the legend's handles.
-
- Second element is a list of bounding boxes for all the patches in
- the legend's handles.
- """
- # should always hold because function is only called internally
- assert self.isaxes
-
- ax = self.parent
- bboxes = []
- lines = []
- offsets = []
-
- for handle in ax.lines:
- assert isinstance(handle, Line2D)
- path = handle.get_path()
- trans = handle.get_transform()
- tpath = trans.transform_path(path)
- lines.append(tpath)
-
- for handle in ax.patches:
- assert isinstance(handle, Patch)
-
- if isinstance(handle, Rectangle):
- transform = handle.get_data_transform()
- bboxes.append(handle.get_bbox().transformed(transform))
- else:
- transform = handle.get_transform()
- bboxes.append(handle.get_path().get_extents(transform))
-
- for handle in ax.collections:
- transform, transOffset, hoffsets, paths = handle._prepare_points()
-
- if len(hoffsets):
- for offset in transOffset.transform(hoffsets):
- offsets.append(offset)
-
- try:
- vertices = np.concatenate([l.vertices for l in lines])
- except ValueError:
- vertices = np.array([])
-
- return [vertices, bboxes, lines, offsets]
-
def draw_frame(self, b):
'b is a boolean. Set draw frame to b'
self.set_frame_on(b)
@@ -891,45 +839,6 @@ class Legend(Artist):
anchored_box = bbox.anchored(c, container=container)
return anchored_box.x0, anchored_box.y0
- def _find_best_position(self, width, height, renderer, consider=None):
- """
- Determine the best location to place the legend.
-
- `consider` is a list of (x, y) pairs to consider as a potential
- lower-left corner of the legend. All are display coords.
- """
- # should always hold because function is only called internally
- assert self.isaxes
-
- verts, bboxes, lines, offsets = self._auto_legend_data()
-
- bbox = Bbox.from_bounds(0, 0, width, height)
- if consider is None:
- consider = [self._get_anchored_bbox(x, bbox,
- self.get_bbox_to_anchor(),
- renderer)
- for x in range(1, len(self.codes))]
-
- candidates = []
- for idx, (l, b) in enumerate(consider):
- legendBox = Bbox.from_bounds(l, b, width, height)
- badness = 0
- # XXX TODO: If markers are present, it would be good to
- # take them into account when checking vertex overlaps in
- # the next line.
- badness = (legendBox.count_contains(verts)
- + legendBox.count_contains(offsets)
- + legendBox.count_overlaps(bboxes)
- + sum(line.intersects_bbox(legendBox, filled=False)
- for line in lines))
- if badness == 0:
- return l, b
- # Include the index to favor lower codes in case of a tie.
- candidates.append((badness, idx, (l, b)))
-
- _, _, (l, b) = min(candidates)
- return l, b
-
def contains(self, event):
return self.legendPatch.contains(event)
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback