fix(math): keep from_range invertible for collapsed pixel spans
A zero pixel span previously produced sx/sy == 0, so to_data_* returned NaN or +/-inf. axis_scale now substitutes a unit pixel span (1.0) when |p1 - p0| <= 1e-12, keeping the mapping finite and invertible while leaving non-collapsed spans byte-identical. Add committed coverage for from_range (linear, flipped pixel axis, log x, degenerate data range, collapsed pixel span) and log10_positive clamping.
This commit is contained in:
1 parent
8a7e9e97f8
commit
ed71719df7
2 files changed
+84
-2
No files matched your search
+22
-2
@@ -15,6 +15,26 @@ void axis_ends(Range r, bool log, f64& lo, f64& hi) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@@ -30,12 +50,12 @@ Transform Transform::from_range(Range x, Range y, f64 px0, f64 px1, f64 py0, f64
|
||||
|
||||
f64 x_lo = 0.0, x_hi = 0.0;
|
||||
axis_ends(x, lx, x_lo, x_hi);
|
||||
t.sx = (px1 - px0) / (x_hi - x_lo);
|
||||
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 = (py1 - py0) / (y_hi - y_lo);
|
||||
t.sy = axis_scale(y_lo, y_hi, py0, py1);
|
||||
t.ty = py0 - y_lo * t.sy;
|
||||
|
||||
return t;
|
||||
|
||||
Reference in new issue
Block a user