planviz
The figure library for OpenPlan Labs — grid maps, search animations, plan timelines and benchmark charts, in the Frontier palette, light and dark from the same call.
Planners not included. This draws results; it does not produce them. It knows
nothing about any solver's types: a grid is a 2-D array where truthy means
blocked, a path is a sequence of (row, col) pairs, and a benchmark series is
a mapping. Anything that can produce those can be plotted.
That figure is the reason this package exists. Every search algorithm has the same three sets — the nodes you expanded, the nodes on the frontier, and the path you returned — so the brand's three colours are that legend, and the three sets differ by shape as well as hue, which is what survives greyscale printing and red/green colour blindness. Drawing it correctly by hand, in four repositories, twice each for light and dark, was the duplication this replaces.
Install
pip install planviz
Two dependencies, both of which a plotting library obviously needs:
matplotlib>=3.7 and numpy>=1.24. Python 3.10 or newer.
Not yet on PyPI — until then:
pip install git+https://github.com/openplan-labs/planviz.
GIF and MP4 writing needs a little more:
pip install 'planviz[animation]' # pillow for GIF, a bundled ffmpeg for MP4
Quickstart
import planviz
grid = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0]] # truthy = blocked
ax = planviz.draw_search(
expanded=[(0, 0), (1, 0), (2, 0), (2, 1)],
frontier=[(0, 1), (2, 2)],
path=[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)],
grid=grid,
dark=True,
)
planviz.save(ax, "search.png")
Four things hold across the whole API:
- Importing
planvizchanges no matplotlib state. The style is applied byplanviz.use_style(dark=...), or per-figure inside astyle_contextthat restores rcParams on exit. A solver library can depend on this without repainting its user's notebook. - Every figure function takes
dark: bool = Falseand an optionalax=, so light and dark variants come from one call and figures compose into panels. - Nothing is saved or shown for you. Functions return the
Axesthey drew on (or theFigure, for multi-panel figures).planviz.save(...)is the explicit write. - The brand ships inside the wheel.
planviz/tokens.pyandplanviz/styles/frontier.mplstyleare generated fromopenplan-labs/brandingand checked for drift in CI, so nothing looks up a repository at runtime and a figure rendered offline matches one rendered on a laptop.
To style figures you draw yourself with the same values:
import matplotlib.pyplot as plt
from planviz import tokens
with planviz.style_context(dark=False) as t:
fig, ax = plt.subplots()
ax.plot(xs, ys, color=t.path) # the solution: the only warm value
ax.plot(xs, others, color=t.agent(0)) # supporting series: the agent ramp
Gallery
Every image below is generated by examples/gallery.py
from synthetic data, in both schemes, and is regenerated in CI. The full
gallery with code for each figure is at
openplan-labs.github.io/planviz/gallery.
Grids and agents
draw_grid |
draw_paths |
draw_paths(highlight=...) |
draw_heatmap |
animate_search and animate_paths. The frontier moves, the expanded set
accumulates, and the path appears once at the end and stays — the accumulated
closed list is the cost of the search, so erasing it hides the thing the
figure is arguing about. GIFs are capped at 12 fps and 800 px wide, because
they are read in a README on a train.
Search progress
search_panels |
radial_wavefront |
plan_timeline |
plan_timeline(timeline_from_paths(...)) |
Benchmarks
scaling_curve |
success_heatmap |
phase_breakdown |
crossover_plot |
The API
| Style | use_style(dark=False), style_context(dark=False), STYLE_PATH |
| Tokens | planviz.tokens — LIGHT, DARK, AGENT_RAMP, Tokens.agent(i), Tokens.sequential() |
| Grids and agents | draw_grid, draw_paths, draw_search, draw_heatmap, animate_paths, animate_search |
| Search progress | search_progress, search_panels, radial_wavefront, plan_timeline, timeline_from_paths, Step |
| Benchmarks | scaling_curve, success_heatmap, phase_breakdown, throughput_curve, crossover_plot |
| Output | save, save_animation, to_jshtml |
Full signatures: API reference.
What it does not do
- No solver adapters. There is no
plot_solution(pymapf.Solution). Callers pass arrays and mappings, which is what keeps one library serving four repositories with different problem types. - No grouped-bar chart and no parity scatter yet.
pymapf'splot_cost_comparisonandcuplan's_fig_qualityhave no home here in 1.0.0; see migration. - No 3-D space-time cube.
pymapf.viz.plot_spacetimestays where it is. - No live views.
LiveSolveViewand jupyddl'sLiveSearchPlotare solver observers, not figures; they belong with the solver they observe. - No interactivity, no web renderer. Static matplotlib output, plus GIF and MP4.
Who uses this
| Repository | What it draws with planviz |
|---|---|
| pymapf | grid maps, multi-agent routes, plan animations, congestion heatmaps, move/wait timelines, scaling charts |
| cuda-planning | the Experiments figures: scaling curves with min–max bands, coverage heatmaps, device phase breakdowns, throughput saturation, crossovers |
| PythonPDDL | --plot search progress, --tree radial wavefront, --plan-plot plan timelines |
| openplan-bench | the cross-repository comparison charts |
docs/migration.md maps each of their existing functions
onto a planviz call, one line at a time.
Contributing
Bug reports, figures that are wrong, and figures that are missing are all
welcome — see CONTRIBUTING.md. The binding constraint is
brand/figures.md;
the parts of it this library enforces are summarised in
design rules.
MIT licensed.
Metadata
Release files for planviz 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| planviz-1.0.0.tar.gz | 58.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| planviz-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 100.0 kB
Release files / planviz-1.0.0.tar.gz
| Download URL | planviz-1.0.0.tar.gz |
|---|---|
| Size | 58.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca3bc01d718e967fa28af7a641ade85e74388df698083c90b82359bdf7f58074
|
|
BLAKE2b-256 checksum How to use checksums |
a8ca625ef4766c0f987fd2788ff9d038446d950595453f4c97a514f956a00e3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.
Transparency logRelease files / planviz-1.0.0-py3-none-any.whl
| Download URL | planviz-1.0.0-py3-none-any.whl |
|---|---|
| Size | 41.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ecda40561f02d805cf366724e6c55676145a51a2578dbe5d1c0f12df1bab2056
|
|
BLAKE2b-256 checksum How to use checksums |
a781e16008ace705872e6c9c1a4048daf908af067d317cb7d2101e87324fe795
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.
Transparency log