// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #include #include namespace plot { void Axis::format_value(f64 v, char* buf, usize n) const { // The one place a value on this axis becomes text, so the origin is applied here and // everything that goes through it -- tick labels, and the crosshair's readout -- reads in // the same frame of reference as the axis itself. // The subtraction leaves a residue when the value and the origin are the same distant number // (a tick exactly on the origin then formatted as `1.78e-12ms` instead of `0ms`). Snapping it // is a *relative* test, so a genuinely small value on a large axis is not swallowed -- what // this removes is arithmetic dust, not data. f64 shown = v - origin; if (std::fabs(shown) < 1.0e-12 * std::max(1.0, std::fabs(v))) shown = 0.0; if (format == TickFormat::Time) { format_time(shown, buf, n); return; } format_si(shown, buf, n); } void Axis::refresh(f32 px, f32 min_px, bool include_minors) { // The range first, so a following origin is this frame's edge and not the last one's: the // zero tick then sits exactly on the window start, which is the whole point of asking. if (origin_follows_range) origin = range.min; generate_ticks(range, px, min_px, ticks, include_minors, origin); if (format != TickFormat::Time) return; // generate_ticks labels with format_si; a time axis relabels its majors here, // because the tick generator has no business knowing about a clock -- through the one // function that says how a value on this axis is written. char buf[64]; for (Tick& tick : ticks) { if (!tick.major) continue; format_value(tick.value, buf, sizeof buf); tick.label = buf; } } } // namespace plot