113 lines
6.4 KiB
C++
113 lines
6.4 KiB
C++
#pragma once
|
|
#include <plot/core/color.hpp>
|
|
|
|
namespace plot::render {
|
|
|
|
/// Every colour and metric the renderer draws with. Defaults are the dark theme
|
|
/// the plan's render smoke test asserts against; a caller swaps a field and the
|
|
/// next frame picks it up, so nothing here is baked into a shader.
|
|
///
|
|
/// ## Units: every metric below is **logical**, and `device()` converts it
|
|
///
|
|
/// A metric a user perceives as a *size* -- a font, a stroke width, a padding, a
|
|
/// marker, a swatch, the minimum spacing between ticks -- is written in logical
|
|
/// units, so a 12 px label is the same physical size on a 1x panel and a 2x one
|
|
/// and a 1 px grid is the same hairline on both. The host states what its display
|
|
/// is with `ui_scale` (device pixels per logical unit) and every consumer converts
|
|
/// exactly once, through `device()`: `Plot::layout()` when it sizes the label
|
|
/// strips, the title strips and the legend's chrome, the renderer when it converts
|
|
/// widths and font px.
|
|
///
|
|
/// The one exception is `dense_threshold_spp`, which is a **density** rather than a
|
|
/// size: samples per *rasterised* pixel. It is compared against a samples-per-pixel
|
|
/// ratio computed from the framebuffer's own width, so scaling it would make the
|
|
/// chosen LOD level depend on the display and not on the data. It is used raw.
|
|
///
|
|
/// The same rule classifies the metrics that live outside this struct, which are
|
|
/// logical too and converted by the same call:
|
|
///
|
|
/// * `Legend`'s pad, swatch, gap and margin;
|
|
/// * `Trace`'s (and `TraceDesc`'s) `width` -- a stroke like `line_width_px`, and
|
|
/// the one a plot draws most of, so leaving it raw would invert the
|
|
/// trace/axis stroke hierarchy at any scale above 1;
|
|
/// * the renderer's marker arm lengths and text offsets.
|
|
struct Style {
|
|
// --- colours: neither logical nor device, so nothing to scale ------------
|
|
|
|
Color background = Color::hex(0x101418);
|
|
Color grid_major = Color::hex(0x2A3038);
|
|
Color grid_minor = Color::hex(0x1C2126);
|
|
Color axis_line = Color::hex(0x8A94A6);
|
|
Color axis_text = Color::hex(0xC8D0DC);
|
|
Color cursor = Color::hex(0xFFB020);
|
|
Color measure_fill = Color::hex(0xFFB020, 0.15f);
|
|
/// The crosshair's colour: its two lines, its markers' own colour excepted, and the
|
|
/// readout panel's border (spec §15). Deliberately **not** `cursor`: the cursor accent is
|
|
/// an orange that a caller's own cursors, a region's bounds and its fill all already use,
|
|
/// while the crosshair is a passive hover instrument that has to read as *chrome* over a
|
|
/// dense trace rather than as another handle to grab. A pale cyan does that against the
|
|
/// dark default theme, and it is a colour no default element of a frame shares.
|
|
Color crosshair = Color::hex(0xB8E8FF);
|
|
|
|
// --- the scale ----------------------------------------------------------
|
|
|
|
/// Device pixels per logical unit. 1.0 is the 1x display every default here was
|
|
/// chosen on. Supplied by the host (`Renderer::style()` is what an adapter
|
|
/// writes), and handed to the plot by `Renderer::draw()` before it lays out, so
|
|
/// layout and drawing cannot disagree about it.
|
|
f32 ui_scale = 1.0f;
|
|
|
|
// --- logical metrics: a size a user perceives, scaled by `device()` ------
|
|
|
|
f32 axis_font_px = 12.0f; ///< logical em the axis text is drawn at.
|
|
f32 grid_label_pad = 4.0f; ///< logical gap between a tick label and the axes,
|
|
///< and the inset inside a title strip.
|
|
/// The two grid classes, each with its own width and its own switch. The grid's
|
|
/// *positions* come from the tick set alone -- `Grid::build()` tags every segment
|
|
/// with the class of the tick it came from -- so these four decide only how a
|
|
/// class is drawn. A class switched off stages no geometry at all: the renderer
|
|
/// skips it before the first segment is staged, so a dense plot's minor grid
|
|
/// costs nothing rather than being drawn transparent (spec §11). The colours
|
|
/// above are already per class.
|
|
f32 grid_major_width = 1.0f; ///< logical width of a major (labelled-tick) line.
|
|
f32 grid_minor_width = 1.0f; ///< logical width of a minor (subdivision) line.
|
|
bool grid_major_visible = true; ///< stage the major grid at all.
|
|
bool grid_minor_visible = true; ///< stage the minor grid at all.
|
|
f32 line_width_px = 1.5f; ///< logical width of the strokes framing the data
|
|
///< (axis lines, cursor markers).
|
|
/// Logical width of a *draggable handle* drawn highlighted: the tab a hovered or
|
|
/// grabbed cursor marker is widened to, and the tab a region's hot bound is drawn
|
|
/// as -- the latter sized by `Plot::layout()` with this same metric, so the two
|
|
/// kinds of handle are one width. Wider than `line_width_px` on purpose: the cue
|
|
/// for "this marker will follow your drag" has to be visible on a marker whose
|
|
/// own colour may already be the accent.
|
|
f32 handle_width_px = 5.0f;
|
|
f32 tick_min_px = 60.0f; ///< logical minimum spacing between major ticks, so
|
|
///< a 2x display gets the same tick *count*.
|
|
|
|
// --- device metric: a density, never scaled ------------------------------
|
|
|
|
f32 dense_threshold_spp = 1.5f; ///< spec §4.3: samples per **device** pixel at or
|
|
///< above which a trace is gathered from the LOD
|
|
///< pyramid instead of from its raw samples. A
|
|
///< density, not a size: it is deliberately not
|
|
///< scaled by `ui_scale`, or the chosen level
|
|
///< would move with the display's DPI. It selects
|
|
///< the gather level, never a primitive: both
|
|
///< sides draw through the one instanced
|
|
///< anti-aliased quad path.
|
|
|
|
/// `logical` in device pixels: the single conversion point for every logical
|
|
/// metric above, so the arithmetic appears once per consumer instead of a `*
|
|
/// ui_scale` scattered over the renderer and the layout.
|
|
///
|
|
/// A scale that is not positive is not a scale -- a host that has not measured
|
|
/// its display, a field nobody filled in -- and converts 1:1 rather than
|
|
/// collapsing every strip, font and stroke to nothing.
|
|
f32 device(f32 logical) const noexcept {
|
|
return ui_scale > 0.0f ? logical * ui_scale : logical;
|
|
}
|
|
};
|
|
|
|
} // namespace plot::render
|