Files
plotter/src/math/transform.cpp
T
Martino Ferrari ed3a8ce645 licence: MIT LICENSE and SPDX identifiers in every first-party file
The repository had no licence at all, so every source was all-rights-reserved
by default. Add the verbatim MIT text at LICENSE (Copyright (c) 2026 Martino
Ferrari) and a two-line SPDX header to each of the 113 first-party sources --
48 include/plot, 26 src, 10 adapters, 5 examples, 21 tests, 3 tools -- so the
grant travels with each file rather than relying on the root document alone.

Scripts keep their shebang on line 1; the SPDX lines follow it. Genuinely
vendored code is untouched: third_party/stb_truetype.h carries no header of
ours. src/render/embedded_mono_font.cpp is generated, so tools/fonts/gen_atlas.py
emit() now writes the two lines into its header comment and the file was
regenerated -- gen_atlas.py --check passes and the regeneration adds only those
comment lines to the previously committed file.

README's provenance section now states MIT and links LICENSE.

No code, test or public API change.
2026-09-28 22:13:40 +02:00

68 lines
2.2 KiB
C++

// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 Martino Ferrari
#include <plot/math/transform.hpp>
namespace plot::math {
namespace {
/// Resolve the endpoints of one axis in transform space, widening a degenerate
/// range by +/-0.5 so the mapping stays invertible.
void axis_ends(Range r, bool log, f64& lo, f64& hi) noexcept {
lo = log ? Transform::log10_positive(r.min) : r.min;
hi = log ? Transform::log10_positive(r.max) : r.max;
if (!(hi > lo)) {
lo -= 0.5;
hi += 0.5;
}
}
/// Scale mapping `[lo,hi]` (transform space, already widened by `axis_ends`)
/// onto the pixel interval `[p0,p1]`.
///
/// A collapsed pixel span (`|p1 - p0| <= kMinPixelSpan`, e.g. a zero-height
/// panel or an untouched layout) would yield a zero scale, so `to_data_*` would
/// divide by zero and return NaN or +/-inf. Instead the span is substituted by
/// the unit span `p0 -> p0 + 1`: the scale is then finite and non-zero and the
/// mapping stays invertible (`to_data_x(to_screen_x(v)) == v`), the axis simply
/// occupying one pixel rather than none. Non-collapsed spans are used verbatim,
/// so ordinary ranges are unaffected.
f64 axis_scale(f64 lo, f64 hi, f64 p0, f64 p1) noexcept {
/// Pixel coordinates in this library are O(1e3), so 1e-12 is
/// indistinguishable from zero while staying far below any span a real
/// viewport can produce.
constexpr f64 kMinPixelSpan = 1e-12;
const f64 span = p1 - p0;
const f64 effective_span = std::fabs(span) <= kMinPixelSpan ? 1.0 : span;
return effective_span / (hi - lo);
}
} // namespace
f64 Transform::log10_positive(f64 v) noexcept {
constexpr f64 kMin = 1e-300;
return std::log10(v < kMin ? kMin : v);
}
Transform Transform::from_range(Range x, Range y, f64 px0, f64 px1, f64 py0, f64 py1, bool lx,
bool ly) noexcept {
Transform t;
t.log_x = lx;
t.log_y = ly;
f64 x_lo = 0.0, x_hi = 0.0;
axis_ends(x, lx, x_lo, x_hi);
t.sx = axis_scale(x_lo, x_hi, px0, px1);
t.tx = px0 - x_lo * t.sx;
f64 y_lo = 0.0, y_hi = 0.0;
axis_ends(y, ly, y_lo, y_hi);
t.sy = axis_scale(y_lo, y_hi, py0, py1);
t.ty = py0 - y_lo * t.sy;
return t;
}
} // namespace plot::math