#pragma once #include #include namespace plot::math { /// Affine data->screen mapping: `screen = data * s + t`, applied after `log10` /// when the corresponding log flag is set. struct Transform { f64 sx = 1.0, tx = 0.0; f64 sy = 1.0, ty = 0.0; bool log_x = false, log_y = false; f64 to_screen_x(f64 x) const noexcept { const f64 v = log_x ? log10_positive(x) : x; return v * sx + tx; } f64 to_screen_y(f64 y) const noexcept { const f64 v = log_y ? log10_positive(y) : y; return v * sy + ty; } f64 to_data_x(f64 screen_x) const noexcept { const f64 v = (screen_x - tx) / sx; return log_x ? std::pow(10.0, v) : v; } f64 to_data_y(f64 screen_y) const noexcept { const f64 v = (screen_y - ty) / sy; return log_y ? std::pow(10.0, v) : v; } /// Set scale/offset so `x` and `y` map onto `[px0,px1]` and `[py0,py1]`. /// A degenerate range is widened by +/-0.5 in the (possibly logarithmic) /// coordinate so the resulting mapping always stays invertible. static Transform from_range(Range x, Range y, f64 px0, f64 px1, f64 py0, f64 py1, bool lx, bool ly) noexcept; /// `log10` of `v`, with `v` clamped to at least 1e-300 first. static f64 log10_positive(f64 v) noexcept; }; } // namespace plot::math namespace plot { using math::Transform; }