5.0 KiB
Architecture
plot is one library, plot_core, plus optional adapter targets. The public headers are
grouped into seven directories, and the umbrella header include/plot/plot.hpp includes
them in this order:
core / math → gl → data → render → plot → input
with the host adapters (adapters/glfw, adapters/imgui, adapters/qt6, adapters/gtk4)
built on top. This page says what each layer owns and the rules the build enforces.
What each layer owns
| layer | directory | owns |
|---|---|---|
core |
include/plot/core/, src/core/ |
the vocabulary: f32/f64/u32/usize, kInvalid, Range, Rect, Color, Span, and Status/ErrorCode for fallible operations. |
math |
include/plot/math/, src/math/ |
Vec2, Mat3, and Transform — the data↔pixel affine mapping, including the log-axis handling and its invertibility guarantee. |
gl |
include/plot/gl/, src/gl/ |
a hand-written OpenGL surface: enumerator typedefs, the entry-point table plot::gl::load() fills, plot::gl::caps(), and the move-only RAII wrappers (gl::Buffer, VertexArray, Program, Texture, Framebuffer, Fence). No GL loader library is used. |
data |
include/plot/data/, src/data/ |
the sample stores: the Series interface, ArraySeries, StreamSeries, RingBuffer, and the min/max LOD MinMaxPyramid. |
render |
include/plot/render/, src/render/ |
presentation: Style, the Renderer, the text machinery (TextBackend, TextAtlas, EmbeddedMonoFont), and the shader/program/buffer/vertex-array primitives. |
plot |
include/plot/plot/, src/plot/ |
the scene model: Plot, Figure, and the primitives they own — Axis, Trace, Cursor, Region, Measure, Legend, Crosshair, Annotation, Grid, Ticks. Pure CPU state, no GL. |
input |
include/plot/input/, src/input/ |
Controller and the host-neutral event types (PointerEvent, WheelEvent, KeyEvent); turns events into gestures. |
| adapters | adapters/*/ |
one per host: they own the window/context/event loop and translate their toolkit's events into input events. |
The dependency rules
plot_coreis third-party-free. ItsCMakeLists.txtlinks only${CMAKE_DL_LIBS}, and the public headers underinclude/plot/include no third-party header. The one exception is the optionalplot::font_ttftarget, which compiles the vendoredthird_party/stb_truetype.h(stb_truetype v1.26, public domain) in its own object libraryplot_stb_truetype, whose include directory isPRIVATE— so nothing else in the build, and no consumer, can reach it by accident.PLOT_BUILD_TTF_FONTdefaults to on exactly when the vendored header is present.- Every public header compiles standalone, and
plot.hppincludes every one of them.tests/CMakeLists.txtgenerates one translation unit per header (globbed, so a new header is picked up automatically), builds an umbrella TU, and registers ctest cases (plot_headers_self_contained,plot_umbrella_covers_public_headers,plot_umbrella_check) that fail the build if either rule breaks. - The umbrella stops at the library. It never includes an adapter header, so
#include <plot/plot.hpp>cannot drag in GLFW, ImGui, Qt or GTK. The adapter headers are reached as<glfw/glfw_host.hpp>,<imgui/imgui_plot.hpp>,<qt6/qt_plot_widget.hpp>and<gtk4/gtk_plot_area.hpp>. - The renderer never owns the context or the event loop.
Renderernever creates, binds or resizes a framebuffer:begin_frame(fb_w, fb_h, viewport_px)draws into whatever framebuffer is bound, clears exactly the given rectangle (scissored, so a host that painted the rest of its surface keeps it), andend_frame()turns the GL state it moved back off. The renderer also never sees a pointer, a button or a modifier — it draws the handles the plot reports as hot, and only a host that drives the plot with aninput::Controllerever makes anything hot. Owning the window, the context and the loop is the adapter's job (or, for a library user, the application's).
One direction is worth stating precisely, because it is the one place the layer order is
not a strict include chain: Renderer (in render/) includes plot/plot.hpp and
plot/figure.hpp, because drawing is being handed a Plot or a Figure to draw. The
plot layer names only render's value types — render::Style and
render::TextBackend — and never names Renderer. So the runtime direction is
"the renderer knows the plot, the plot does not know the renderer".
The design record
The specifications and implementation plans — the requirements, the numbered spec sections
this documentation sometimes refers to, and the reasoning behind decisions — live under
docs/superpowers/ (specs/ and plans/). That tree is the project's
design record, not user documentation: it is where a decision is argued, not where the
library is explained. This docs/ guide and docs/coverage.md are the
user-facing documents.