Skip to content

Commit e64f3fb

Browse files
committed
Legend autopositioning with "spiraling" lines.
Fix computation of the intersection between a line and a prospective legend position by passing ``filled=False`` to ``line.intersects_bbox``. This fixes autopositioning of the legend e.g. with plt.plot([0, 0, 1, 1, 0], [0, 1, 1, 0, 0], label="foo") plt.legend() where the legend should be placed "inside" the area enclosed by the line.
1 parent 424d3b0 commit e64f3fb

File tree

2 files changed

+16
-34
lines changed

2 files changed

+16
-34
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
More accurate legend autopositioning
2+
````````````````````````````````````
3+
4+
Automatic positioning of legends now prefers using the area surrounded by a `Line2D` rather than placing the legend over the line itself.

lib/matplotlib/legend.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -939,47 +939,25 @@ def _find_best_position(self, width, height, renderer, consider=None):
939939
renderer)
940940
for x in range(1, len(self.codes))]
941941

942-
# tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y()
943-
944942
candidates = []
945-
for l, b in consider:
943+
for idx, (l, b) in enumerate(consider):
946944
legendBox = Bbox.from_bounds(l, b, width, height)
947945
badness = 0
948946
# XXX TODO: If markers are present, it would be good to
949-
# take their into account when checking vertex overlaps in
947+
# take them into account when checking vertex overlaps in
950948
# the next line.
951-
badness = legendBox.count_contains(verts)
952-
badness += legendBox.count_contains(offsets)
953-
badness += legendBox.count_overlaps(bboxes)
954-
for line in lines:
955-
# FIXME: the following line is ill-suited for lines
956-
# that 'spiral' around the center, because the bbox
957-
# may intersect with the legend even if the line
958-
# itself doesn't. One solution would be to break up
959-
# the line into its straight-segment components, but
960-
# this may (or may not) result in a significant
961-
# slowdown if lines with many vertices are present.
962-
if line.intersects_bbox(legendBox):
963-
badness += 1
964-
965-
ox, oy = l, b
949+
badness = (legendBox.count_contains(verts)
950+
+ legendBox.count_contains(offsets)
951+
+ legendBox.count_overlaps(bboxes)
952+
+ sum(line.intersects_bbox(legendBox, filled=False)
953+
for line in lines))
966954
if badness == 0:
967-
return ox, oy
968-
969-
candidates.append((badness, (l, b)))
970-
971-
# rather than use min() or list.sort(), do this so that we are assured
972-
# that in the case of two equal badnesses, the one first considered is
973-
# returned.
974-
# NOTE: list.sort() is stable.But leave as it is for now. -JJL
975-
minCandidate = candidates[0]
976-
for candidate in candidates:
977-
if candidate[0] < minCandidate[0]:
978-
minCandidate = candidate
979-
980-
ox, oy = minCandidate[1]
955+
return l, b
956+
# Include the index to favor lower codes in case of a tie.
957+
candidates.append((badness, idx, (l, b)))
981958

982-
return ox, oy
959+
_, _, (l, b) = min(candidates)
960+
return l, b
983961

984962
def contains(self, event):
985963
return self.legendPatch.contains(event)

0 commit comments

Comments
 (0)