feat(render): logical style metrics with a UI scale, so chrome has a consistent physical size
This commit is contained in:
1 parent
1f03631e98
commit
7500fc90d2
21 files changed
+708
-82
No files matched your search
@@ -6,7 +6,29 @@ 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, and 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);
|
||||
@@ -14,16 +36,47 @@ struct Style {
|
||||
Color axis_text = Color::hex(0xC8D0DC);
|
||||
Color cursor = Color::hex(0xFFB020);
|
||||
Color measure_fill = Color::hex(0xFFB020, 0.15f);
|
||||
f32 axis_font_px = 12.0f;
|
||||
f32 grid_label_pad = 4.0f;
|
||||
f32 line_width_px = 1.5f;
|
||||
f32 dense_threshold_spp = 1.5f; ///< spec §4.3: samples per pixel at or above
|
||||
///< which a trace is gathered from the LOD
|
||||
///< pyramid instead of from its raw samples.
|
||||
///< It selects the gather level, never a
|
||||
///< primitive: both sides draw through the one
|
||||
///< instanced anti-aliased quad path.
|
||||
f32 tick_min_px = 60.0f; ///< minimum spacing between major ticks.
|
||||
|
||||
// --- 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.
|
||||
f32 line_width_px = 1.5f; ///< logical width of the strokes framing the data
|
||||
///< (axis lines, cursor markers).
|
||||
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
|
||||
Reference in new issue
Block a user