feat(plot): Figure with stacked rows and a synchronised x axis

This commit is contained in:
Martino Ferrari committed 2026-09-28 14:42:17 +02:00
1 parent 7cc06f07f5
commit 8f1e60a105
11 files changed
+1665 -24

No files matched your search

+45 -2
View File
@@ -7,6 +7,7 @@
#include <plot/core/status.hpp>
#include <plot/core/types.hpp>
#include <plot/math/vec2.hpp>
#include <plot/plot/figure.hpp>
#include <plot/plot/plot.hpp>
#include <plot/render/embedded_mono_font.hpp>
#include <plot/render/buffer.hpp>
@@ -130,6 +131,25 @@ class Renderer {
/// vertical strokes and the label is a staged glyph run, so a frame with regions
/// leaves behind exactly what a frame without them does.
void draw(Plot& plot);
/// Draws a whole `Figure`: every row in its own rect, then the figure's one shared
/// x axis.
/// Each row is drawn by the *same* path as `draw(Plot&)` -- the viewport and the
/// x-label policy come from `Figure::layout()`, which the figure hands out and
/// which lays every row out against its rect -- so a row is chrome and data drawn
/// exactly as a stand-alone plot is, and `draw(Plot&)` itself is unchanged for
/// single-plot hosts.
/// The shared axis is the frame's last text: when the figure says labels belong on
/// the bottom only (the default), the rows reserved no x label strip of their own
/// and the labels of the last row's x axes are staged once, in the strip
/// `FigureLayout::x_axis_strip` measured, under the last row. With that switched
/// off, every row draws its own labels and this draws no shared axis at all.
/// Styling, the text backend and `ui_scale` are installed on **every** row before
/// the figure computes its layout, so the strips the rows measure and the strokes
/// this draws are one set of numbers, exactly as they are for one plot.
void draw(Figure& figure);
/// Ends the frame, draining `glGetError()` in a loop so a stale error cannot be
/// attributed to a later frame (see `last_frame_had_gl_error()`).
@@ -320,11 +340,34 @@ class Renderer {
/// the pen -- for the rotated run, the bottom-left corner of its first cell.
f32 stage_text_clipped(Vec2 pos_px, const char* utf8, f32 budget_px, Vec2 axis);
/// Pins the text pipeline for a frame: the plot layer's backend at the size the
/// style asks for, the cell every staged glyph is drawn in, and the atlas the
/// backend publishes -- re-uploaded when its revision moved. False when a driver
/// refused the re-upload, which the frame reports (see `last_frame_had_gl_error()`)
/// and carries on from, drawing with the atlas it already had.
bool begin_frame_text() noexcept;
/// Hands `plot` everything the frame draws with: the renderer's text backend (a
/// host's is replaced here, named in a debug build, as it always was), the style,
/// and the `ui_scale` the plot converts that style's logical metrics by. Called
/// before a plot is laid out, which is why it is separate from the drawing.
void prepare_plot(Plot& plot);
/// Draws one plot's frame with the layout the caller already computed:
/// `draw(Plot&)` passes `plot.layout()`, and `draw(Figure&)` the layout
/// `Figure::layout()` computed for that row -- so a figure's rows are drawn by
/// exactly this path and neither entry point lays a plot out twice.
void draw_plot(Plot& plot, const PlotLayout& layout);
void draw_grid(const Plot& plot);
void draw_axis_lines(const Plot& plot);
/// The chrome helpers take the frame's layout: `plot.layout()` recomputes the
/// ticks, the strips and the grid, so it belongs to `draw()` alone.
/// The chrome helpers take the frame's layout: `Plot::layout()` recomputes the
/// ticks, the strips and the grid, so it is computed once, in `draw()`, and handed
/// down -- never re-derived per helper.
void draw_tick_labels(const Plot& plot, const PlotLayout& layout);
/// The x tick labels of every x axis of `plot`, centred on their ticks in `strip`:
/// what `draw_tick_labels()` uses with the plot's own `x_label_area`, and what
/// `draw(Figure&)` uses with the figure's shared strip -- one rule for a plot's
/// labels and a figure's, so the two can never place a label differently.
void draw_x_tick_labels(const Plot& plot, const Rect& strip);
void draw_titles(const Plot& plot, const PlotLayout& layout);
/// The data helpers take it too; they read the axes rect and the transforms it
/// pins.