plot
An embeddable OpenGL time-series plotting library for real-time, high-sample-rate data.
The core (plot::core, C++20, OpenGL 3.3 core) has no third-party dependency at all;
the host integrations that own a window and an event loop are separate, opt-in targets
built on top of it.
Hosts, and what a frame costs
| host | CMake target | source |
|---|---|---|
| GLFW | plot::glfw |
adapters/glfw/ |
| Dear ImGui | plot::imgui (build-tree only) |
adapters/imgui/ |
| Qt6 | plot::qt6 |
adapters/qt6/ |
| GTK4 | plot::gtk4 |
adapters/gtk4/ |
Per-frame rendering cost is O(visible pixels + samples produced since the last frame),
not O(samples): a trace denser than one sample per pixel is drawn from a min/max LOD
pyramid rather than from raw samples. The plot_multiplot_demo run stages 18,651 GPU
segments to draw ~4.5 million visible samples across six traces — the frame is
bounded by the pixels it lights, not by the sample count behind them. That figure is from
a linux-release build under xvfb-run on llvmpipe (Mesa's software rasteriser); no
run touched a GPU, so read it as an upper bound and a record of the software path.
Features
- grid with independent major/minor line classes (
Style::grid_major_visible,grid_minor_visible); - x and y axes, with a vertical (bottom-to-top) y-axis title;
- cursors in both orientations — vertical markers and horizontal rulers;
- draggable regions and measurement bands with exact interval statistics;
- a signal legend anchored to an axes corner;
- a hover crosshair with a per-signal, interpolated readout;
- a multi-plot
Figureof stacked rows sharing one synchronised x axis; - live follow (
XFollow::Live) with relative (multiplicative) zoom and mouse panning; - DPI-consistent sizing: metrics are logical units converted once through
Style::ui_scale; - an anti-aliased embedded monospaced font, plus an optional runtime TrueType backend
(
plot::font_ttf).
Build and install
Needs a C++20 compiler and CMake 3.24+. Presets live in CMakePresets.json
(linux-debug, linux-release, linux-asan, linux-coverage).
cmake --preset linux-release
cmake --build build/release
cmake --install build/release --prefix /where/you/want
Install is on by default (PLOT_INSTALL=ON) and installs the plot_core archive, the
public headers under include/plot/, and a CMake package config under lib/cmake/plot/.
A consumer then needs only:
find_package(plot 0.1 REQUIRED)
target_link_libraries(my_app PRIVATE plot::core)
Embedding
A host owns the window, the GL context and the event loop; plot owns none of them. This
is the whole of a minimal GLFW program — the adapter opens the window, runs the loop and
feeds the controller, and the callback is where live data would be pushed:
#include <glfw/glfw_host.hpp> // the optional GLFW adapter
#include <plot/plot.hpp>
#include <cmath>
#include <vector>
int main() {
plot::glfw::Host host;
plot::Plot& p = host.plot();
std::vector<plot::f32> y(512);
for (plot::usize i = 0; i < y.size(); ++i)
y[i] = static_cast<plot::f32>(std::sin(0.05 * static_cast<double>(i)));
plot::data::ArraySeries series(std::move(y), 0.0, 1.0);
const plot::u32 x = p.add_x_axis({"x", {0.0, 511.0}, true});
const plot::u32 sig = p.add_y_axis({"signal", {-1.0, 1.0}, true, plot::AxisPlace::Left});
p.add_trace({&series, plot::Color::hex(0x4FA3FF), 1.5f, x, sig, "sine"});
plot::Status st;
return host.run({800, 480, "plot"}, [](plot::Plot&, plot::f64) {}, &st) ? 0 : 1;
}
It compiles and links against a built tree (see docs/getting-started.md
for the same program written against plot::core directly, with no adapter):
g++ -std=c++20 -I include -I adapters readme_main.cpp \
build/debug/adapters/glfw/libplot_glfw.a build/debug/libplot_core.a \
$(pkg-config --libs glfw3) -ldl -lpthread -o plot_readme
Documentation
The user guide lives under docs/; docs/superpowers/ is the design record, not user
documentation.
- Getting started — prerequisites, build/install/consume, and a
minimal GLFW program against
plot::core. - Architecture — the layers (
core/math→gl→data→render→plot→input), what each owns, and the dependency rules. - Embedding into a host — the four adapters, the
ui_scale/DPI rule per host, and the headless demo invocations. - Data and performance — the sample stores, the single-producer/single-consumer contract, the LOD pyramid, and the measured frame times.
- Interaction — cursors, regions, the measurement band, the legend, the crosshair, the gesture table, and live follow.
- Styling — every
Stylefield with its default and unit, the grid classes, and installing a text backend. - Building and testing — presets,
PLOT_BUILD_*options, the suite, the headless demo modes, and the coverage command. - Coverage — the coverage method and the measured per-file table.
Testing and coverage
cmake --preset linux-debug
cmake --build build/debug
ctest --test-dir build/debug --output-on-failure # 283 cases, 0 failed
tools/coverage.sh # configure+build+run+capture+floor check
The linux-debug suite runs 283 cases; the linux-coverage preset builds 272 of
them, because its adapters are off and 11 cases need an adapter target (8 in
test_imgui_widget.cpp, 3 in test_glfw_host.cpp). Coverage is enforced by
tools/coverage.sh against tools/coverage_floor.py: 80% per file under src/ and
90% aggregate. The current measurement is an aggregate 95.3% (3284/3447 lines) over
23 files, every file at or above 80%; the per-file table is in
docs/coverage.md, which the command regenerates.
Licence and third-party provenance
- This library ships no licence file — there is no
LICENSE/COPYINGin the repository, so the sources carry no granted licence and a distributor must add one. - Adwaita Mono (the embedded font,
render::EmbeddedMonoFont) is under SIL OFL 1.1, committed verbatim atlicences/OFL-1.1.txt. The backend class is named neutrally (EmbeddedMonoFont, not "Adwaita Mono") because OFL reserves the font name for modified redistributions. The baked atlas is generated at authoring time bytools/fonts/gen_atlas.pyand committed as data, soplot_corerasterises nothing at build or run time. - stb_truetype v1.26 (public domain, Sean Barrett / RAD Game Tools) is vendored at
third_party/stb_truetype.hand compiled only by the optionalplot_font_ttftarget; its include directory isPRIVATE, so no consumer and no other target can reach it.PLOT_BUILD_TTF_FONTdefaults on exactly when the vendored header is present. plot_coreis third-party-free. It links only${CMAKE_DL_LIBS}, and no public header underinclude/plot/includes a third-party header. The header self-containment and umbrella checks intests/CMakeLists.txtfail the build if either rule breaks.
Limitations
- Timings are llvmpipe under Xvfb (
tools/coverage.sh, the demo summaries). No measurement here used a GPU; there are no hardware numbers. - macOS and Windows are untested. Development and CI are Linux/EGL + X11 only.
- Fractional-scale compositing and touchpad scroll calibration are unverified — the
ui_scalesources are correct per host, but no run exercised a real fractional DPI or a calibrated scroll device. - Untrusted font files are out of scope for the runtime
plot::font_ttfbackend: stb_truetype does no range checking, sofrom_file()must only be handed files the host trusts. render↔plotis not a strict include chain.Rendererincludesplot/plot.hppbecause drawing is being handed aPlot; theplotlayer names onlyrender's value types (Style,TextBackend), neverRenderer. The cycle and its direction are documented in docs/architecture.md.