From b140c65e6e5bb2b314ca50d6861dc4e7f985f1cc Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 9 Dec 2018 13:34:50 +0100 Subject: [PATCH] Make Text._get_layout simpler to follow. - Instead of constructing and accessing multidimensional arrays whs and horizLayout by (opaque) index, construct separate lists with more meaningful names. - Define xmin/xmax/ymin/ymax/width/height closer to their place of use. - Make computation of offsetx and offsety more symmetric between rotation_mode == "anchor" and == "default". --- lib/matplotlib/text.py | 112 ++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 7cd48ef06b6a..b8be61588353 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -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. + 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,16 +370,16 @@ 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': @@ -386,8 +387,8 @@ def _get_layout(self, renderer): 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