Files
plotter/docs/architecture.md
T

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_core is third-party-free. Its CMakeLists.txt links only ${CMAKE_DL_LIBS}, and the public headers under include/plot/ include no third-party header. The one exception is the optional plot::font_ttf target, which compiles the vendored third_party/stb_truetype.h (stb_truetype v1.26, public domain) in its own object library plot_stb_truetype, whose include directory is PRIVATE — so nothing else in the build, and no consumer, can reach it by accident. PLOT_BUILD_TTF_FONT defaults to on exactly when the vendored header is present.
  • Every public header compiles standalone, and plot.hpp includes every one of them. tests/CMakeLists.txt generates 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. Renderer never 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), and end_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 an input::Controller ever 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.