-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make Text._get_layout simpler to follow. #12951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -276,86 +276,87 @@ def _get_layout(self, renderer): | |
if key in self._cached: | ||
return self._cached[key] | ||
|
||
horizLayout = [] | ||
|
||
thisx, thisy = 0.0, 0.0 | ||
xmin, ymin = 0.0, 0.0 | ||
width, height = 0.0, 0.0 | ||
lines = self.get_text().split('\n') | ||
lines = self.get_text().split("\n") # Ensures lines is not empty. | ||
|
||
whs = np.zeros((len(lines), 2)) | ||
horizLayout = np.zeros((len(lines), 4)) | ||
ws = [] | ||
hs = [] | ||
xs = [] | ||
ys = [] | ||
|
||
# Full vertical extent of font, including ascenders and descenders: | ||
tmp, lp_h, lp_bl = renderer.get_text_width_height_descent('lp', | ||
self._fontproperties, | ||
ismath=False) | ||
offsety = (lp_h - lp_bl) * self._linespacing | ||
_, lp_h, lp_d = renderer.get_text_width_height_descent( | ||
"lp", self._fontproperties, ismath=False) | ||
min_dy = (lp_h - lp_d) * self._linespacing | ||
|
||
baseline = 0 | ||
for i, line in enumerate(lines): | ||
clean_line, ismath = self.is_math_text(line, self.get_usetex()) | ||
if clean_line: | ||
w, h, d = renderer.get_text_width_height_descent(clean_line, | ||
self._fontproperties, | ||
ismath=ismath) | ||
w, h, d = renderer.get_text_width_height_descent( | ||
clean_line, self._fontproperties, ismath=ismath) | ||
else: | ||
w, h, d = 0, 0, 0 | ||
w = h = d = 0 | ||
|
||
# For multiline text, increase the line spacing when the | ||
# text net-height(excluding baseline) is larger than that | ||
# of a "l" (e.g., use of superscripts), which seems | ||
# what TeX does. | ||
# For multiline text, increase the line spacing when the text | ||
# net-height (excluding baseline) is larger than that of a "l" | ||
# (e.g., use of superscripts), which seems what TeX does. | ||
h = max(h, lp_h) | ||
d = max(d, lp_bl) | ||
d = max(d, lp_d) | ||
|
||
whs[i] = w, h | ||
ws.append(w) | ||
hs.append(h) | ||
|
||
# Metrics of the last line that are needed later: | ||
baseline = (h - d) - thisy | ||
|
||
if i == 0: | ||
# position at baseline | ||
thisy = -(h - d) | ||
else: | ||
# put baseline a good distance from bottom of previous line | ||
thisy -= max(offsety, (h - d) * self._linespacing) | ||
horizLayout[i] = thisx, thisy, w, h | ||
thisy -= max(min_dy, (h - d) * self._linespacing) | ||
|
||
xs.append(thisx) # == 0. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alterantively
outside of the loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the symmetry of handling between xs and ys is nicer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO |
||
ys.append(thisy) | ||
|
||
thisy -= d | ||
width = max(width, w) | ||
descent = d | ||
|
||
# Metrics of the last line that are needed later: | ||
descent = d | ||
|
||
# Bounding box definition: | ||
width = max(ws) | ||
xmin = 0 | ||
xmax = width | ||
ymax = 0 | ||
# ymin is baseline of previous line minus the descent of this line | ||
ymin = horizLayout[-1][1] - descent | ||
ymin = ys[-1] - descent # baseline of last line minus its descent | ||
height = ymax - ymin | ||
xmax = xmin + width | ||
|
||
# get the rotation matrix | ||
M = Affine2D().rotate_deg(self.get_rotation()) | ||
|
||
offsetLayout = np.zeros((len(lines), 2)) | ||
offsetLayout[:] = horizLayout[:, 0:2] | ||
# now offset the individual text lines within the box | ||
if len(lines) > 1: # do the multiline aligment | ||
malign = self._get_multialignment() | ||
if malign == 'center': | ||
offsetLayout[:, 0] += width / 2.0 - horizLayout[:, 2] / 2.0 | ||
elif malign == 'right': | ||
offsetLayout[:, 0] += width - horizLayout[:, 2] | ||
malign = self._get_multialignment() | ||
if malign == 'left': | ||
offset_layout = [(x, y) for x, y in zip(xs, ys)] | ||
elif malign == 'center': | ||
offset_layout = [(x + width / 2 - w / 2, y) | ||
for x, y, w in zip(xs, ys, ws)] | ||
elif malign == 'right': | ||
offset_layout = [(x + width - w, y) | ||
for x, y, w in zip(xs, ys, ws)] | ||
|
||
# the corners of the unrotated bounding box | ||
cornersHoriz = np.array( | ||
[(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)], float) | ||
corners_horiz = np.array( | ||
[(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]) | ||
|
||
# now rotate the bbox | ||
cornersRotated = M.transform(cornersHoriz) | ||
|
||
txs = cornersRotated[:, 0] | ||
tys = cornersRotated[:, 1] | ||
|
||
corners_rotated = M.transform(corners_horiz) | ||
# compute the bounds of the rotated box | ||
xmin, xmax = txs.min(), txs.max() | ||
ymin, ymax = tys.min(), tys.max() | ||
xmin = corners_rotated[:, 0].min() | ||
xmax = corners_rotated[:, 0].max() | ||
ymin = corners_rotated[:, 1].min() | ||
ymax = corners_rotated[:, 1].max() | ||
width = xmax - xmin | ||
height = ymax - ymin | ||
|
||
|
@@ -369,25 +370,25 @@ def _get_layout(self, renderer): | |
# compute the text location in display coords and the offsets | ||
# necessary to align the bbox with that location | ||
if halign == 'center': | ||
offsetx = (xmin + width / 2.0) | ||
offsetx = (xmin + xmax) / 2 | ||
elif halign == 'right': | ||
offsetx = (xmin + width) | ||
offsetx = xmax | ||
else: | ||
offsetx = xmin | ||
|
||
if valign == 'center': | ||
offsety = (ymin + height / 2.0) | ||
offsety = (ymin + ymax) / 2 | ||
elif valign == 'top': | ||
offsety = (ymin + height) | ||
offsety = ymax | ||
elif valign == 'baseline': | ||
offsety = ymin + descent | ||
elif valign == 'center_baseline': | ||
offsety = ymin + height - baseline / 2.0 | ||
else: | ||
offsety = ymin | ||
else: | ||
xmin1, ymin1 = cornersHoriz[0] | ||
xmax1, ymax1 = cornersHoriz[2] | ||
xmin1, ymin1 = corners_horiz[0] | ||
xmax1, ymax1 = corners_horiz[2] | ||
|
||
if halign == 'center': | ||
offsetx = (xmin1 + xmax1) / 2.0 | ||
|
@@ -415,12 +416,9 @@ def _get_layout(self, renderer): | |
bbox = Bbox.from_bounds(xmin, ymin, width, height) | ||
|
||
# now rotate the positions around the first x,y position | ||
xys = M.transform(offsetLayout) | ||
xys -= (offsetx, offsety) | ||
|
||
xs, ys = xys[:, 0], xys[:, 1] | ||
xys = M.transform(offset_layout) - (offsetx, offsety) | ||
|
||
ret = bbox, list(zip(lines, whs, xs, ys)), descent | ||
ret = bbox, list(zip(lines, zip(ws, hs), *xys.T)), descent | ||
self._cached[key] = ret | ||
return ret | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gets rid of the need for
enumerate()
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not immediately obvious that thisy cannot be zero for the second line too even if the first line is empty (it's true, but only thanks to the
h = max(h, lp_h)
/d = max(d, lp_d)
lines) so I think leaving it as it is is clearer.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just by common sense, the vertical position
thisy
must increase also for empty lines. So it should only be zero before handling the first line.But ok, if you want to leave it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think common sense applies to this function (or to a lot of matplotlib, sadly). (e.g. if you look at the way center_baseline is defined a bit below, it looks weirdly different between anchor and non-anchor rotations...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funnily that got fixed by #13029 :)