Skip to content

Commit 43e0a8c

Browse files
committed
cairo_circles.
1 parent b0c2116 commit 43e0a8c

File tree

6 files changed

+79
-38
lines changed

6 files changed

+79
-38
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
v0.1a2
22
======
33

4-
- Switched to ``dlopen``\ing Raqm.
4+
- Switch to ``dlopen``\ing Raqm.
5+
- Allow control of circle-drawing algorithm.
6+
- Add ``set_option``, ``get_option``.
7+
- Various bug, performance, and build fixes.
58

69
v0.1a1
710
======

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ other hand, this is currently the only way in which the webagg-based backends
248248
(e.g., Jupyter's inline widget) are supported.
249249

250250
At import-time, mplcairo will attempt to load Raqm_. The use of that library
251-
can be controlled and checked using the ``load_raqm``, ``unload_raqm``, and
252-
``has_raqm`` functions.
251+
can be controlled and checked using ``set_options`` and ``get_options``
252+
functions.
253253

254254
The ``examples`` directory contains a few cases where the output of this
255255
renderer is arguably more accurate than the one of the default renderer, Agg:

lib/mplcairo/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
except ImportError:
1212
pass
1313

14-
1514
if sys.platform != "win32":
1615
def _load_symbols():
1716
# dlopen() pycairo's extension module with RTLD_GLOBAL to dynamically
@@ -22,6 +21,12 @@ def _load_symbols():
2221

2322
_load_symbols()
2423

25-
from ._mplcairo import antialias_t, load_raqm, unload_raqm, has_raqm
24+
from ._mplcairo import antialias_t, get_options, set_options
25+
26+
__all__ = ["antialias_t", "get_options", "set_options"]
2627

27-
__all__ = ["antialias_t", "load_raqm", "unload_raqm", "has_raqm"]
28+
set_options(cairo_circles=True)
29+
try:
30+
set_options(raqm=True)
31+
except RuntimeError:
32+
pass

src/_mplcairo.cpp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,11 +1433,6 @@ PYBIND11_MODULE(_mplcairo, m)
14331433
{
14341434
m.doc() = "A cairo backend for matplotlib.";
14351435

1436-
try {
1437-
load_raqm();
1438-
} catch (std::runtime_error&) {
1439-
}
1440-
14411436
// Setup global values.
14421437

14431438
#ifndef _WIN32
@@ -1473,8 +1468,6 @@ PYBIND11_MODULE(_mplcairo, m)
14731468

14741469
FT_CHECK(FT_Init_FreeType, &detail::ft_library);
14751470

1476-
detail::UNIT_CIRCLE =
1477-
py::module::import("matplotlib.path").attr("Path").attr("unit_circle")();
14781471
detail::PIXEL_MARKER =
14791472
py::module::import("matplotlib.markers").attr("MarkerStyle")(",");
14801473
GraphicsContextRenderer::mathtext_parser_ =
@@ -1526,14 +1519,43 @@ PYBIND11_MODULE(_mplcairo, m)
15261519

15271520
// Export functions.
15281521
m.def(
1529-
"load_raqm", &load_raqm,
1530-
"Load raqm. Raises an exception on failure.");
1531-
m.def(
1532-
"unload_raqm", &unload_raqm,
1533-
"Unload raqm. Raises an exception on failure.");
1522+
"set_options", [](py::kwargs kwargs) -> void {
1523+
// FIXME[pybind11]: Redo once they pybind11 has kwonly args.
1524+
auto pop_option = [&](std::string key) -> std::optional<bool> {
1525+
return kwargs.attr("pop")(key, py::none()).cast<std::optional<bool>>();
1526+
};
1527+
if (auto cairo_circles = pop_option("cairo_circles")) {
1528+
detail::UNIT_CIRCLE =
1529+
*cairo_circles
1530+
? py::module::import("matplotlib.path").attr("Path")
1531+
.attr("unit_circle")()
1532+
: py::none{};
1533+
}
1534+
if (auto raqm = pop_option("raqm")) {
1535+
if (*raqm) {
1536+
load_raqm();
1537+
} else {
1538+
unload_raqm();
1539+
}
1540+
}
1541+
if (py::bool_(kwargs)) {
1542+
throw std::runtime_error("Unknown options passed to set_options");
1543+
}
1544+
}, R"__doc__(
1545+
Set mplcairo options. The following options are available:
1546+
- cairo_circles: Use cairo's circle drawing algorithm, rather than Matplotlib's
1547+
fixed spline approximation.
1548+
- raqm: Use Raqm for text rendering.
1549+
)__doc__");
15341550
m.def(
1535-
"has_raqm", &has_raqm,
1536-
"Return whether raqm is loaded.");
1551+
"get_options", []() -> py::dict {
1552+
return py::dict(
1553+
"cairo_circles"_a=!detail::UNIT_CIRCLE.is(py::none{}),
1554+
"raqm"_a=has_raqm());
1555+
}, R"__doc__(
1556+
Get current mplcairo options. See `set_mplcairo` for a description of
1557+
available options.
1558+
)__doc__");
15371559

15381560
// Export classes.
15391561

src/_util.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ ps_surface_set_eps_t cairo_ps_surface_set_eps;
3636
ps_surface_dsc_comment_t cairo_ps_surface_dsc_comment;
3737

3838
cairo_user_data_key_t const REFS_KEY{}, STATE_KEY{}, FT_KEY{};
39-
py::object UNIT_CIRCLE{}, PIXEL_MARKER{};
39+
py::object UNIT_CIRCLE{py::none{}}, PIXEL_MARKER{py::none{}};
4040
}
4141

4242
rgba_t AdditionalState::get_hatch_color() {

tests/test_speed.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77

88
from matplotlib.backends.backend_agg import FigureCanvasAgg
9+
import mplcairo
910
from mplcairo import antialias_t
1011
from mplcairo.base import FigureCanvasCairo
1112

@@ -17,6 +18,7 @@
1718
@pytest.fixture
1819
def axes():
1920
mpl.rcdefaults()
21+
mplcairo.set_options(cairo_circles=True, raqm=False)
2022
return Figure().subplots()
2123

2224

@@ -37,7 +39,7 @@ def sample_image():
3739

3840

3941
@pytest.mark.parametrize("canvas_cls", _canvas_classes)
40-
def test_axes(benchmark, canvas_cls, axes):
42+
def test_axes(benchmark, axes, canvas_cls):
4143
axes.figure.canvas = canvas_cls(axes.figure)
4244
benchmark(axes.figure.canvas.draw)
4345

@@ -54,7 +56,7 @@ def test_axes(benchmark, canvas_cls, axes):
5456
(FigureCanvasCairo, antialias_t.BEST)])
5557
@pytest.mark.parametrize("joinstyle", ["miter", "round", "bevel"])
5658
def test_line(
57-
benchmark, canvas_cls, antialiased, joinstyle, axes, sample_vectors):
59+
benchmark, axes, sample_vectors, canvas_cls, antialiased, joinstyle):
5860
with mpl.rc_context({"agg.path.chunksize": 0}):
5961
axes.plot(*sample_vectors,
6062
antialiased=antialiased, solid_joinstyle=joinstyle)
@@ -67,23 +69,34 @@ def test_line(
6769
# code path for circles which may not be representative of general performance.
6870

6971

70-
@pytest.mark.parametrize("canvas_cls", _canvas_classes)
71-
@pytest.mark.parametrize("threshold", [1 / 8, 0])
72-
@pytest.mark.parametrize("marker", ["o", "s"])
73-
def test_markers(
74-
benchmark, canvas_cls, threshold, marker, axes, sample_vectors):
72+
_marker_test_parametrization = pytest.mark.parametrize(
73+
"canvas_cls, threshold, marker, cairo_circles", [
74+
(FigureCanvasAgg, 0, "o", False),
75+
(FigureCanvasAgg, 0, "s", False),
76+
(FigureCanvasCairo, 0, "o", False),
77+
(FigureCanvasCairo, 0, "o", True),
78+
(FigureCanvasCairo, 0, "s", False),
79+
(FigureCanvasCairo, 1 / 8, "o", False),
80+
(FigureCanvasCairo, 1 / 8, "o", True),
81+
(FigureCanvasCairo, 1 / 8, "s", False),
82+
]
83+
)
84+
85+
86+
@_marker_test_parametrization
87+
def test_markers(benchmark, axes, sample_vectors,
88+
canvas_cls, threshold, marker, cairo_circles):
89+
mplcairo.set_options(cairo_circles=cairo_circles)
7590
with mpl.rc_context({"path.simplify_threshold": threshold}):
7691
axes.plot(*sample_vectors, marker=marker)
7792
despine(axes)
7893
axes.figure.canvas = canvas_cls(axes.figure)
7994
benchmark(axes.figure.canvas.draw)
8095

8196

82-
@pytest.mark.parametrize("canvas_cls", _canvas_classes)
83-
@pytest.mark.parametrize("threshold", [1 / 8, 0])
84-
@pytest.mark.parametrize("marker", ["o", "s"])
85-
def test_scatter_multicolor(
86-
benchmark, canvas_cls, threshold, marker, axes, sample_vectors):
97+
@_marker_test_parametrization
98+
def test_scatter_multicolor(benchmark, axes, sample_vectors,
99+
canvas_cls, threshold, marker, cairo_circles):
87100
with mpl.rc_context({"path.simplify_threshold": threshold}):
88101
a, b = sample_vectors
89102
axes.scatter(a, a, c=b, marker=marker)
@@ -92,11 +105,9 @@ def test_scatter_multicolor(
92105
benchmark(axes.figure.canvas.draw)
93106

94107

95-
@pytest.mark.parametrize("canvas_cls", _canvas_classes)
96-
@pytest.mark.parametrize("threshold", [1 / 8, 0])
97-
@pytest.mark.parametrize("marker", ["o", "s"])
98-
def test_scatter_multisize(
99-
benchmark, canvas_cls, threshold, marker, axes, sample_vectors):
108+
@_marker_test_parametrization
109+
def test_scatter_multisize(benchmark, axes, sample_vectors,
110+
canvas_cls, threshold, marker, cairo_circles):
100111
with mpl.rc_context({"path.simplify_threshold": threshold}):
101112
a, b = sample_vectors
102113
axes.scatter(a, a, s=100 * b ** 2, marker=marker)

0 commit comments

Comments
 (0)