Skip to content

Commit

Permalink
Update matplotlib_display.py for latest matplotlib
Browse files Browse the repository at this point in the history
Fixes #80.
  • Loading branch information
georgestagg committed Jan 10, 2025
1 parent 9936d85 commit 6197a24
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 37 deletions.
2 changes: 1 addition & 1 deletion _extensions/live/resources/live-runtime.js

Large diffs are not rendered by default.

95 changes: 59 additions & 36 deletions live-runtime/src/scripts/Python/matplotlib_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

import math
import numpy as np
from functools import lru_cache
from matplotlib.backend_bases import (
FigureCanvasBase,
FigureManagerBase,
RendererBase,
GraphicsContextBase,
_Backend,
)
from matplotlib.cbook import maxdict
from matplotlib._enums import CapStyle
from matplotlib.font_manager import findfont
from matplotlib.ft2font import LOAD_NO_HINTING, FT2Font
from matplotlib.mathtext import MathTextParser
Expand Down Expand Up @@ -94,12 +95,20 @@ def restore(self):
self.renderer.ctx.restore()

def set_capstyle(self, cs):
if isinstance(cs, str):
cs = CapStyle(cs)
# Convert the JoinStyle enum to its name if needed
if hasattr(cs, "name"):
cs = cs.name.lower()
if cs in ["butt", "round", "projecting"]:
self._capstyle = cs
self.renderer.ctx.lineCap = _capstyle_d[cs]
else:
raise ValueError(f"Unrecognized cap style. Found {cs}")

def get_capstyle(self):
return self._capstyle

def set_clip_rectangle(self, rectangle):
self.renderer.ctx.save()
if not rectangle:
Expand Down Expand Up @@ -153,8 +162,8 @@ def __init__(self, ctx, width, height, dpi, fig):
self.ctx.width = self.width
self.ctx.height = self.height
self.dpi = dpi
self.fontd = maxdict(50)
self.mathtext_parser = MathTextParser("bitmap")
self.mathtext_parser = MathTextParser("path")
self._get_font_helper = lru_cache(maxsize=50)(self._get_font_helper)

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

return CSS_color

def _draw_math_text(self, gc, x, y, s, prop, angle):
width, height, depth, glyphs, rects = self.mathtext_parser.parse(
s, dpi=self.dpi, prop=prop
)
self.ctx.save()
self.ctx.translate(x, self.height + y)
if angle != 0:
self.ctx.rotate(-math.radians(angle))
self.ctx.fillStyle = self._matplotlib_color_to_CSS(
gc.get_rgb(), gc.get_alpha(), gc.get_forced_alpha()
)
for font, fontsize, c, ox, oy in glyphs:
self.ctx.save()
self.ctx.translate(ox, -oy)
font.set_size(fontsize, self.dpi)
font.load_char(c)
verts, codes = font.get_path()
path = Path(verts, codes)
transform = Affine2D().scale(1.0, -1.0)
self._path_helper(self.ctx, path, transform)
self.ctx.fill()
self.ctx.restore()
for x1, y1, x2, y2 in rects:
self.ctx.fillRect(x1, -y2, x2 - x1, y2 - y1)
self.ctx.restore()

def _set_style(self, gc, rgbFace=None):
if rgbFace is not None:
self.ctx.fillStyle = self._matplotlib_color_to_CSS(
rgbFace, gc.get_alpha(), gc.get_forced_alpha()
)

if gc.get_capstyle():
self.ctx.lineCap = _capstyle_d[gc.get_capstyle()]
capstyle = gc.get_capstyle()
if capstyle:
# Get the string name if it's an enum
if hasattr(capstyle, "name"):
capstyle = capstyle.name.lower()
self.ctx.lineCap = _capstyle_d[capstyle]

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

def _get_font_helper(self, prop):
fname = findfont(prop)
font = FT2Font(str(fname))
font_file_name = fname.rpartition("/")[-1]
return (font, font_file_name)

def _get_font(self, prop):
key = hash(prop)
font_value = self.fontd.get(key)
if font_value is None:
fname = findfont(prop)
font_value = self.fontd.get(fname)
if font_value is None:
font = FT2Font(str(fname))
font_file_name = fname[fname.rfind("/") + 1 :]
font_value = font, font_file_name
self.fontd[fname] = font_value
self.fontd[key] = font_value
font, font_file_name = font_value
result = self._get_font_helper(prop)
font = result[0]
font.clear()
font.set_size(prop.get_size_in_points(), self.dpi)
return font, font_file_name
return result

def get_text_width_height_descent(self, s, prop, ismath):
w: float
h: float
d: float
if ismath:
image, d = self.mathtext_parser.parse(s, self.dpi, prop)
image_arr = np.asarray(image)
h, w = image_arr.shape
# Use the path parser to get exact metrics
width, height, depth, _, _ = self.mathtext_parser.parse(
s, dpi=72, prop=prop
)
return width, height, depth
else:
font, _ = self._get_font(prop)
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
Expand All @@ -271,21 +309,6 @@ def get_text_width_height_descent(self, s, prop, ismath):
d = font.get_descent() / 64.0
return w, h, d

def _draw_math_text(self, gc, x, y, s, prop, angle):
rgba, descent = self.mathtext_parser.to_rgba(
s, gc.get_rgb(), self.dpi, prop.get_size_in_points()
)
height, width, _ = rgba.shape
angle = math.radians(angle)
if angle != 0:
self.ctx.save()
self.ctx.translate(x, y)
self.ctx.rotate(-angle)
self.ctx.translate(-x, -y)
self.draw_image(gc, x, -y - descent, np.flipud(rgba))
if angle != 0:
self.ctx.restore()

def draw_image(self, gc, x, y, im, transform=None):
import numpy as np
im = np.flipud(im)
Expand Down

0 comments on commit 6197a24

Please sign in to comment.