mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-25 02:30:15 -06:00
Homography::apply, Affine::apply, Warp::to_corrected, Warp::corrected_to_source and embedded_warp are now generic over lightcraft_geom::Real (private *_real variants in pipeline, public apply_real in geom); the f64 API is a thin wrapper, so callers are unchanged. Real is implemented for f64 (plain IEEE operators) and for Interval, which rounds every bound one ulp outward and turns division by a range containing zero, the homography's 1e-300 denominator clamp and non-finite bounds into "undecided" (NaN). Because the interval runs the same operations as the f64 path, it bounds the rounded f64 results, not only the exact ones. The f64 path keeps the same operations in the same order (only commuted operands, which IEEE + and x leave bit-identical; no fma). Proof: warp_f64_results_are_bit_identical_to_the_pre_generic_formulas hashes the bits of 2000 random warps x 40 points through every formula (plus the clamp branch, including +-1e-300 exactly) and matches the digest computed by the same test on thumb-split (419261c) before the change; reassociating one expression, or `<=` for `<` in the clamp, changes it. The framing chain (output px -> transformed -> lens-corrected -> green source, then inside the edges) is written once: Warp::framing, generic. Warp::frame (f64, returns the corrected and green source points) serves both the CPU resample (sample_warped) and Warp::covers; block_coverage evaluates the same chain with intervals over a whole block of output pixels and compares the bounds against the same edges() inside uses, with no margin: the bounds enclose every pixel's f64 result, so the decision equals the per-pixel one; else None. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>