docs: a user guide covering getting started, architecture, embedding, data, interaction, styling and testing

This commit is contained in:
Martino Ferrari committed 2026-09-28 21:50:19 +02:00
1 parent d03f9ce51a
commit b7d0f901cc
7 files changed
+1082

No files matched your search

+68
View File
@@ -0,0 +1,68 @@
# 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/`](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](coverage.md) are the
user-facing documents.