Skip to content

Instantly share code, notes, and snippets.

@f0k
Last active July 18, 2016 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save f0k/f8c87ffd82488c2ed303989592bf4ebd to your computer and use it in GitHub Desktop.
Save f0k/f8c87ffd82488c2ed303989592bf4ebd to your computer and use it in GitHub Desktop.
Matplotlib PGF backend with support for interpolation='none'
# -*- coding: utf-8 -*-
"""
Version of the PGF backend that supports interpolation='none' for imshow().
To use it, copy it somewhere it can be found by Python and set the backend
to 'module://backend_mypgf'. For example, this can be done by:
import matplotlib as mpl
mpl.use('module://backend_mypgf')
Only requires two functions to be overridden in RendererPgf, but needs to
import or override a lot of additional boilerplate.
Author: Jan Schlüter
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import os
import numpy as np
import matplotlib as mpl
from matplotlib.backends.backend_pgf import (FigureCanvasPgf,
FigureManagerPgf,
RendererPgf,
writeln,
draw_if_interactive,
get_preamble,
get_fontspec,
MixedModeRenderer,
Figure,
_png,
)
class MyRendererPgf(RendererPgf):
def option_scale_image(self):
"""
pgf backend supports arbitrary scaling of image.
"""
return True
def draw_image(self, gc, x, y, im, dx=None, dy=None, trans=None):
# save the images to png files
path = os.path.dirname(self.fh.name)
fname = os.path.splitext(os.path.basename(self.fh.name))[0]
fname_img = "%s-img%d.png" % (fname, self.image_counter)
self.image_counter += 1
_png.write_png(np.array(im)[::-1], os.path.join(path, fname_img))
# reference the image in the pgf picture
writeln(self.fh, r"\begin{pgfscope}")
self._print_pgf_clip(gc)
h, w = im.get_size_out()
if dy:
h = dy
if dx:
w = dx
f = 1. / self.dpi # from display coords to inch
if (trans is not None and
not isinstance(trans, mpl.transforms.IdentityTransform)):
t1, t2, t3, t4, t5, t6 = trans.frozen().to_values()
writeln(self.fh, r"\makeatletter\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}\makeatother" % (t1, t2, t3, t4, t5 * f, t6 * f))
interp = str(trans is None).lower()
writeln(self.fh, r"\pgftext[at=\pgfqpoint{%fin}{%fin},left,bottom]{\pgfimage[interpolate=%s,width=%fin,height=%fin]{%s}}" % (x * f, y * f, interp, w * f, h * f, fname_img))
writeln(self.fh, r"\end{pgfscope}")
class MyFigureCanvasPgf(FigureCanvasPgf):
def _print_pgf_to_fh(self, fh, *args, **kwargs):
if kwargs.get("dryrun", False):
renderer = MyRendererPgf(self.figure, None, dummy=True)
self.figure.draw(renderer)
return
header_text = """%% Creator: Matplotlib, PGF backend
%%
%% To include the figure in your LaTeX document, write
%% \\input{<filename>.pgf}
%%
%% Make sure the required packages are loaded in your preamble
%% \\usepackage{pgf}
%%
%% Figures using additional raster images can only be included by \input if
%% they are in the same directory as the main LaTeX file. For loading figures
%% from other directories you can use the `import` package
%% \\usepackage{import}
%% and then include the figures with
%% \\import{<path to file>}{<filename>.pgf}
%%
"""
# append the preamble used by the backend as a comment for debugging
header_info_preamble = ["%% Matplotlib used the following preamble"]
for line in get_preamble().splitlines():
header_info_preamble.append("%% " + line)
for line in get_fontspec().splitlines():
header_info_preamble.append("%% " + line)
header_info_preamble.append("%%")
header_info_preamble = "\n".join(header_info_preamble)
# get figure size in inch
w, h = self.figure.get_figwidth(), self.figure.get_figheight()
dpi = self.figure.get_dpi()
# create pgfpicture environment and write the pgf code
fh.write(header_text)
fh.write(header_info_preamble)
fh.write("\n")
writeln(fh, r"\begingroup")
writeln(fh, r"\makeatletter")
writeln(fh, r"\begin{pgfpicture}")
writeln(fh, r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" % (w, h))
writeln(fh, r"\pgfusepath{use as bounding box, clip}")
_bbox_inches_restore = kwargs.pop("bbox_inches_restore", None)
renderer = MixedModeRenderer(self.figure, w, h, dpi,
MyRendererPgf(self.figure, fh),
bbox_inches_restore=_bbox_inches_restore)
self.figure.draw(renderer)
# end the pgfpicture environment
writeln(fh, r"\end{pgfpicture}")
writeln(fh, r"\makeatother")
writeln(fh, r"\endgroup")
def get_renderer(self):
return MyRendererPgf(self.figure, None, dummy=True)
###############################################################################
FigureCanvas = MyFigureCanvasPgf
FigureManager = FigureManagerPgf
def new_figure_manager(num, *args, **kwargs):
"""
Create a new figure manager instance
"""
# if a main-level app must be created, this is the usual place to
# do it -- see backend_wx, backend_wxagg and backend_tkagg for
# examples. Not all GUIs require explicit instantiation of a
# main-level app (egg backend_gtk, backend_gtkagg) for pylab
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass(*args, **kwargs)
return new_figure_manager_given_figure(num, thisFig)
def new_figure_manager_given_figure(num, figure):
"""
Create a new figure manager instance for the given figure.
"""
canvas = MyFigureCanvasPgf(figure)
manager = FigureManagerPgf(canvas, num)
return manager
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment