Skip to content

Commit 6197a24

Browse files
committed
Update matplotlib_display.py for latest matplotlib
Fixes #80.
1 parent 9936d85 commit 6197a24

File tree

2 files changed

+60
-37
lines changed

2 files changed

+60
-37
lines changed

_extensions/live/resources/live-runtime.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

live-runtime/src/scripts/Python/matplotlib_display.py

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
import math
66
import numpy as np
7+
from functools import lru_cache
78
from matplotlib.backend_bases import (
89
FigureCanvasBase,
910
FigureManagerBase,
1011
RendererBase,
1112
GraphicsContextBase,
1213
_Backend,
1314
)
14-
from matplotlib.cbook import maxdict
15+
from matplotlib._enums import CapStyle
1516
from matplotlib.font_manager import findfont
1617
from matplotlib.ft2font import LOAD_NO_HINTING, FT2Font
1718
from matplotlib.mathtext import MathTextParser
@@ -94,12 +95,20 @@ def restore(self):
9495
self.renderer.ctx.restore()
9596

9697
def set_capstyle(self, cs):
98+
if isinstance(cs, str):
99+
cs = CapStyle(cs)
100+
# Convert the JoinStyle enum to its name if needed
101+
if hasattr(cs, "name"):
102+
cs = cs.name.lower()
97103
if cs in ["butt", "round", "projecting"]:
98104
self._capstyle = cs
99105
self.renderer.ctx.lineCap = _capstyle_d[cs]
100106
else:
101107
raise ValueError(f"Unrecognized cap style. Found {cs}")
102108

109+
def get_capstyle(self):
110+
return self._capstyle
111+
103112
def set_clip_rectangle(self, rectangle):
104113
self.renderer.ctx.save()
105114
if not rectangle:
@@ -153,8 +162,8 @@ def __init__(self, ctx, width, height, dpi, fig):
153162
self.ctx.width = self.width
154163
self.ctx.height = self.height
155164
self.dpi = dpi
156-
self.fontd = maxdict(50)
157-
self.mathtext_parser = MathTextParser("bitmap")
165+
self.mathtext_parser = MathTextParser("path")
166+
self._get_font_helper = lru_cache(maxsize=50)(self._get_font_helper)
158167

159168
# Keep the state of fontfaces that are loading
160169
self.fonts_loading = {}
@@ -189,14 +198,44 @@ def _matplotlib_color_to_CSS(self, color, alpha, alpha_overrides, is_RGB=True):
189198

190199
return CSS_color
191200

201+
def _draw_math_text(self, gc, x, y, s, prop, angle):
202+
width, height, depth, glyphs, rects = self.mathtext_parser.parse(
203+
s, dpi=self.dpi, prop=prop
204+
)
205+
self.ctx.save()
206+
self.ctx.translate(x, self.height + y)
207+
if angle != 0:
208+
self.ctx.rotate(-math.radians(angle))
209+
self.ctx.fillStyle = self._matplotlib_color_to_CSS(
210+
gc.get_rgb(), gc.get_alpha(), gc.get_forced_alpha()
211+
)
212+
for font, fontsize, c, ox, oy in glyphs:
213+
self.ctx.save()
214+
self.ctx.translate(ox, -oy)
215+
font.set_size(fontsize, self.dpi)
216+
font.load_char(c)
217+
verts, codes = font.get_path()
218+
path = Path(verts, codes)
219+
transform = Affine2D().scale(1.0, -1.0)
220+
self._path_helper(self.ctx, path, transform)
221+
self.ctx.fill()
222+
self.ctx.restore()
223+
for x1, y1, x2, y2 in rects:
224+
self.ctx.fillRect(x1, -y2, x2 - x1, y2 - y1)
225+
self.ctx.restore()
226+
192227
def _set_style(self, gc, rgbFace=None):
193228
if rgbFace is not None:
194229
self.ctx.fillStyle = self._matplotlib_color_to_CSS(
195230
rgbFace, gc.get_alpha(), gc.get_forced_alpha()
196231
)
197232

198-
if gc.get_capstyle():
199-
self.ctx.lineCap = _capstyle_d[gc.get_capstyle()]
233+
capstyle = gc.get_capstyle()
234+
if capstyle:
235+
# Get the string name if it's an enum
236+
if hasattr(capstyle, "name"):
237+
capstyle = capstyle.name.lower()
238+
self.ctx.lineCap = _capstyle_d[capstyle]
200239

201240
self.ctx.strokeStyle = self._matplotlib_color_to_CSS(
202241
gc.get_rgb(), gc.get_alpha(), gc.get_forced_alpha()
@@ -238,30 +277,29 @@ def draw_path(self, gc, path, transform, rgbFace=None):
238277
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
239278
super().draw_markers(gc, marker_path, marker_trans, path, trans, rgbFace)
240279

280+
def _get_font_helper(self, prop):
281+
fname = findfont(prop)
282+
font = FT2Font(str(fname))
283+
font_file_name = fname.rpartition("/")[-1]
284+
return (font, font_file_name)
285+
241286
def _get_font(self, prop):
242-
key = hash(prop)
243-
font_value = self.fontd.get(key)
244-
if font_value is None:
245-
fname = findfont(prop)
246-
font_value = self.fontd.get(fname)
247-
if font_value is None:
248-
font = FT2Font(str(fname))
249-
font_file_name = fname[fname.rfind("/") + 1 :]
250-
font_value = font, font_file_name
251-
self.fontd[fname] = font_value
252-
self.fontd[key] = font_value
253-
font, font_file_name = font_value
287+
result = self._get_font_helper(prop)
288+
font = result[0]
254289
font.clear()
255290
font.set_size(prop.get_size_in_points(), self.dpi)
256-
return font, font_file_name
291+
return result
257292

258293
def get_text_width_height_descent(self, s, prop, ismath):
259294
w: float
260295
h: float
296+
d: float
261297
if ismath:
262-
image, d = self.mathtext_parser.parse(s, self.dpi, prop)
263-
image_arr = np.asarray(image)
264-
h, w = image_arr.shape
298+
# Use the path parser to get exact metrics
299+
width, height, depth, _, _ = self.mathtext_parser.parse(
300+
s, dpi=72, prop=prop
301+
)
302+
return width, height, depth
265303
else:
266304
font, _ = self._get_font(prop)
267305
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
@@ -271,21 +309,6 @@ def get_text_width_height_descent(self, s, prop, ismath):
271309
d = font.get_descent() / 64.0
272310
return w, h, d
273311

274-
def _draw_math_text(self, gc, x, y, s, prop, angle):
275-
rgba, descent = self.mathtext_parser.to_rgba(
276-
s, gc.get_rgb(), self.dpi, prop.get_size_in_points()
277-
)
278-
height, width, _ = rgba.shape
279-
angle = math.radians(angle)
280-
if angle != 0:
281-
self.ctx.save()
282-
self.ctx.translate(x, y)
283-
self.ctx.rotate(-angle)
284-
self.ctx.translate(-x, -y)
285-
self.draw_image(gc, x, -y - descent, np.flipud(rgba))
286-
if angle != 0:
287-
self.ctx.restore()
288-
289312
def draw_image(self, gc, x, y, im, transform=None):
290313
import numpy as np
291314
im = np.flipud(im)

0 commit comments

Comments
 (0)