#include #include #include namespace plot { namespace { /// True when `v` lies in `r`, allowing the rounding slack a generated tick can /// carry at the range's edge: a tick that is within 1e-9 relative of the edge is /// the edge, and dropping it would leave the frame un-drawn. bool inside(f64 v, Range r) noexcept { const f64 tolerance = std::max(std::fabs(r.min), std::fabs(r.max)) * 1e-9 + 1e-12; return v >= r.min - tolerance && v <= r.max + tolerance; } } // namespace void Grid::build(const std::vector& x_ticks, Range x_range, const std::vector& y_ticks, Range y_range) { lines_.clear(); if (!std::isfinite(x_range.min) || !std::isfinite(x_range.max) || !std::isfinite(y_range.min) || !std::isfinite(y_range.max)) { return; } // Segments stay in data space: the renderer applies the transform, so a pan or // zoom needs no grid rebuild other than for a changed tick set. if (vertical) { for (const Tick& tick : x_ticks) { if (!tick.major && !show_minor) continue; if (!std::isfinite(tick.value) || !inside(tick.value, x_range)) continue; lines_.push_back(GridLine{tick.value, y_range.min, tick.value, y_range.max, tick.major}); } } if (horizontal) { for (const Tick& tick : y_ticks) { if (!tick.major && !show_minor) continue; if (!std::isfinite(tick.value) || !inside(tick.value, y_range)) continue; lines_.push_back(GridLine{x_range.min, tick.value, x_range.max, tick.value, tick.major}); } } } } // namespace plot