Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/ci_pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,19 @@ jobs:
# with:
# python-version: ${{ matrix.python-version }}

- name: Install LaTeX
run: |
# pdflatex with pgfplots: the tikzfigure backend, its tests and tutorials
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
texlive-latex-base texlive-latex-extra texlive-pictures \
texlive-fonts-recommended lmodern

- name: Install python dependencies
run: |
python -m pip install --upgrade pip
# tikzfigure 0.4.0 is not on PyPI yet: install it from GitHub (drop once released)
pip install "tikzfigure[vis] @ git+https://github.com/max-models/tikzfigure@c290f43ac1deb4a4ed3b5b40919f10d3a6b53e8b"
pip install ".[dev]"

- name: Run tests
Expand All @@ -37,7 +47,5 @@ jobs:

- name: Test tutorials
run: |
# tutorial_07_tikz.ipynb requires pdflatex — skip it in CI
jupyter nbconvert --to notebook --execute \
$(ls tutorials/*.ipynb | grep -v tutorial_07_tikz) \
jupyter nbconvert --to notebook --execute tutorials/*.ipynb \
--output-dir=/tmp --ExecutePreprocessor.timeout=300
8 changes: 7 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,20 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Install pandoc
- name: Install pandoc and LaTeX
run: |
sudo apt-get update
sudo apt-get install -y pandoc
# pdflatex with pgfplots: the tutorials compile tikzfigure figures
sudo apt-get install -y --no-install-recommends \
texlive-latex-base texlive-latex-extra texlive-pictures \
texlive-fonts-recommended lmodern

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
# tikzfigure 0.4.0 is not on PyPI yet: install it from GitHub (drop once released)
pip install "tikzfigure[vis] @ git+https://github.com/max-models/tikzfigure@c290f43ac1deb4a4ed3b5b40919f10d3a6b53e8b"
pip install ".[docs]"

- name: Build Sphinx docs
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/matplotlib-import.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Matplotlib import compatibility

on:
push:
branches: [main, devel]
pull_request:

jobs:
import-fixtures:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
matplotlib: ['3.8.*', '3.9.*', '3.10.*', '3.11.*']
env:
MPLBACKEND: Agg
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
# tikzfigure 0.4.0 is not on PyPI yet: install it from GitHub (drop once released)
- run: python -m pip install "tikzfigure[vis] @ git+https://github.com/max-models/tikzfigure@c290f43ac1deb4a4ed3b5b40919f10d3a6b53e8b"
- run: python -m pip install '.[test]' 'numpy<2' 'matplotlib==${{ matrix.matplotlib }}'
- run: python -m pytest src/maxplotlib/tests/test_matplotlib_import.py src/maxplotlib/tests/test_matplotlib_import_extended.py
82 changes: 68 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Maxlotlib
# Maxplotlib


# Maxplotlib
Expand Down Expand Up @@ -219,23 +219,29 @@ canvas.show(backend="tikzfigure")

![](README_files/figure-commonmark/cell-14-output-1.png)

### Horizontal Subplots with TikZ Backend
### Subplots and Meshes with the TikZ Backend

The tikzfigure backend supports creating side-by-side subplots (1×n
layouts):
The tikzfigure backend draws the canvas with Matplotlib and converts the
drawn figure into pgfplots axes, so every layout converts (rows, columns,
grids, twin axes), with LaTeX text, legends and colorbars. Lines,
markers, bars and text are pgfplots code; meshes and images are included
as images:

``` python
x = np.linspace(0, 2 * np.pi, 200)
canvas, (ax1, ax2) = Canvas.subplots(ncols=2, width="10cm", ratio=0.3)
canvas, (ax1, ax2) = Canvas.subplots(ncols=2, width="12cm", ratio=0.45)

ax1.plot(x, np.sin(x), color="royalblue")
ax1.set_title("sin(x)")
ax1.plot(x, np.sin(x), color="royalblue", label="$\\sin x$")
ax1.plot(x, np.cos(x), color="tomato", label="$\\cos x$")
ax1.set_title("Lines")
ax1.set_legend(True)

ax2.plot(x, np.cos(x), color="tomato")
ax2.set_title("cos(x)")
xx, yy = np.meshgrid(x, x)
ax2.pcolormesh(xx, yy, np.sin(xx) * np.cos(yy), cmap="RdBu_r")
ax2.add_colorbar(label="$\\sin x \\cos y$")
ax2.set_title("A mesh")

canvas.suptitle("Trigonometric Functions")
canvas.show(backend="tikzfigure") # Generates LaTeX subfigures
canvas.show(backend="tikzfigure") # compiles with pdflatex
```

<div id="fig-showcase-subplots">
Expand All @@ -248,9 +254,11 @@ Figure 2

</div>

**Note:** Only horizontal layouts (1×n) are currently supported with the
tikzfigure backend. Vertical/grid layouts will raise
`NotImplementedError`. See the tutorials for more examples.
`canvas.render(backend="tikzfigure").savefig("figure.tikz")` writes the
code for `\\input` in a LaTeX document, with the images next to it. Any
Matplotlib figure converts the same way with
`maxplotlib.backends.tikzfigure.figure_to_tikz(fig)`. See the tutorials
for more examples.

### Terminal Backend with plotext

Expand Down Expand Up @@ -349,3 +357,49 @@ canvas.show()

(<Figure size 590.551x324.803 with 1 Axes>,
array([[<Axes: xlabel='x'>]], dtype=object))

### xarray data

Plot labelled [xarray](https://docs.xarray.dev) data directly
(`pip install maxplotlibx[xarray]`). Axes come from the coordinates,
labels from the `long_name` and `units` attributes, and titles from the
coordinates you selected. `import maxplotlib.xarray` adds a `.maxplot`
accessor that mirrors xarray’s own `.plot` API and returns an ordinary
`Canvas`, so the backend is still chosen when rendering:

``` python
import xarray as xr

import maxplotlib.xarray # registers da.maxplot and ds.maxplot

t = np.linspace(0, 1.5, 6)
xs = np.linspace(0, 2 * np.pi, 80)
ys = np.linspace(-1, 1, 50)
wave = xr.DataArray(
np.sin(xs - 2 * t[:, None, None]) * np.exp(-3 * ys[None, :, None] ** 2),
dims=("t", "y", "x"),
coords={"t": ("t", t, {"units": "s"}), "y": ys, "x": ("x", xs, {"units": "m"})},
name="phi",
attrs={"long_name": "Potential", "units": "V"},
)

wave.maxplot.pcolormesh(col="t", col_wrap=3, canvas_kwargs={"width": "16cm", "ratio": 0.6}).show()
```

![](README_files/figure-commonmark/cell-20-output-1.png)

(<Figure size 944.882x566.929 with 7 Axes>,
array([[<Axes: title={'center': 't = 0 s'}, ylabel='y'>,
<Axes: title={'center': 't = 0.3 s'}>,
<Axes: title={'center': 't = 0.6 s'}>],
[<Axes: title={'center': 't = 0.9 s'}, xlabel='x [m]', ylabel='y'>,
<Axes: title={'center': 't = 1.2 s'}, xlabel='x [m]'>,
<Axes: title={'center': 't = 1.5 s'}, xlabel='x [m]'>]],
dtype=object))

The same works through Canvas methods,
e.g. `canvas.plot(da, hue="species")`,
`ax.pcolormesh(da, xcoord="R", ycoord="Z")` for curvilinear grids, or
`Canvas.facet(da, col="t")`. `ds.maxplot.scatter(x=..., y=..., hue=...)`
plots one Dataset variable against another. See the [xarray
tutorial](tutorials/tutorial_17_xarray.ipynb) for more.
64 changes: 53 additions & 11 deletions README.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Maxlotlib
title: Maxplotlib
format: gfm
fig-dpi: 150
---
Expand Down Expand Up @@ -194,29 +194,37 @@ Or plot with the TikZ backend:
canvas.show(backend="tikzfigure")
```

### Horizontal Subplots with TikZ Backend
### Subplots and Meshes with the TikZ Backend

The tikzfigure backend supports creating side-by-side subplots (1×n layouts):
The tikzfigure backend draws the canvas with Matplotlib and converts the drawn figure into
pgfplots axes, so every layout converts (rows, columns, grids, twin axes), with LaTeX text,
legends and colorbars. Lines, markers, bars and text are pgfplots code; meshes and images are
included as images:

```{python}
#| label: fig-showcase-subplots
#| fig-width: 9
#| fig-height: 6

x = np.linspace(0, 2 * np.pi, 200)
canvas, (ax1, ax2) = Canvas.subplots(ncols=2, width="10cm", ratio=0.3)
canvas, (ax1, ax2) = Canvas.subplots(ncols=2, width="12cm", ratio=0.45)

ax1.plot(x, np.sin(x), color="royalblue")
ax1.set_title("sin(x)")
ax1.plot(x, np.sin(x), color="royalblue", label="$\\sin x$")
ax1.plot(x, np.cos(x), color="tomato", label="$\\cos x$")
ax1.set_title("Lines")
ax1.set_legend(True)

ax2.plot(x, np.cos(x), color="tomato")
ax2.set_title("cos(x)")
xx, yy = np.meshgrid(x, x)
ax2.pcolormesh(xx, yy, np.sin(xx) * np.cos(yy), cmap="RdBu_r")
ax2.add_colorbar(label="$\\sin x \\cos y$")
ax2.set_title("A mesh")

canvas.suptitle("Trigonometric Functions")
canvas.show(backend="tikzfigure") # Generates LaTeX subfigures
canvas.show(backend="tikzfigure") # compiles with pdflatex
```

**Note:** Only horizontal layouts (1×n) are currently supported with the tikzfigure backend. Vertical/grid layouts will raise `NotImplementedError`. See the tutorials for more examples.
`canvas.render(backend="tikzfigure").savefig("figure.tikz")` writes the code for `\\input` in a
LaTeX document, with the images next to it. Any Matplotlib figure converts the same way with
`maxplotlib.backends.tikzfigure.figure_to_tikz(fig)`. See the tutorials for more examples.

### Terminal Backend with plotext

Expand Down Expand Up @@ -276,3 +284,37 @@ Show all layers:
```{python}
canvas.show()
```

### xarray data

Plot labelled [xarray](https://docs.xarray.dev) data directly
(`pip install maxplotlibx[xarray]`). Axes come from the coordinates, labels
from the `long_name` and `units` attributes, and titles from the coordinates
you selected. `import maxplotlib.xarray` adds a `.maxplot` accessor that mirrors
xarray's own `.plot` API and returns an ordinary `Canvas`, so the backend is
still chosen when rendering:

```{python}
import xarray as xr

import maxplotlib.xarray # registers da.maxplot and ds.maxplot

t = np.linspace(0, 1.5, 6)
xs = np.linspace(0, 2 * np.pi, 80)
ys = np.linspace(-1, 1, 50)
wave = xr.DataArray(
np.sin(xs - 2 * t[:, None, None]) * np.exp(-3 * ys[None, :, None] ** 2),
dims=("t", "y", "x"),
coords={"t": ("t", t, {"units": "s"}), "y": ys, "x": ("x", xs, {"units": "m"})},
name="phi",
attrs={"long_name": "Potential", "units": "V"},
)

wave.maxplot.pcolormesh(col="t", col_wrap=3, canvas_kwargs={"width": "16cm", "ratio": 0.6}).show()
```

The same works through Canvas methods, e.g. `canvas.plot(da, hue="species")`,
`ax.pcolormesh(da, xcoord="R", ycoord="Z")` for curvilinear grids, or
`Canvas.facet(da, col="t")`. `ds.maxplot.scatter(x=..., y=..., hue=...)` plots
one Dataset variable against another. See the
[xarray tutorial](tutorials/tutorial_17_xarray.ipynb) for more.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading