from typing import Sequence from .charts_scale_linear_scale import interpolate, pair from .math_ln import ln from .math_round_float import round_float def log_scale(domain: Sequence[float], range: Sequence[float], value: float, clamp: bool) -> float: """Where value lands on a logarithmic axis: equal ratios take equal lengths. The base cancels out, so there is none to pass. ln is math.ln, not math.log, so every language lands on the same double. """ d0, d1 = pair(domain, "domain") r0, r1 = pair(range, "range") if not (d0 > 0) or not (d1 > 0): raise ValueError(f"log scale domain must be positive; got [{d0}, {d1}]") if d0 == d1: raise ValueError(f"domain has no width: both ends are {d0}") if not (value > 0): raise ValueError(f"value must be positive for a log scale; got {value}") l0 = ln(d0) t = (ln(value) - l0) / (ln(d1) - l0) if clamp: t = max(0.0, min(1.0, t)) return round_float(interpolate(r0, r1, t), 6)