1,653 bytes · the Python implementation · view raw
import math
from typing import Tuple
def tick_step(start: float, stop: float, count: int) -> float:
"""The tick step for about count ticks: 1, 2 or 5 times a power of ten, as d3-array's tickIncrement chooses it, found without log10 and returned as one correctly rounded division so 0.2 is exactly the double 0.2."""
mul, div = tick_spec(start, stop, count)
return mul / div
# Exported for nice_ticks, which needs the step as a multiplier or a divisor.def tick_spec(start: float, stop: float, count: int) -> Tuple[float, float]:
"""The step as (multiplier, divisor), one of which is 1; (0, 1) when start equals stop."""for v in (start, stop):
if isinstance(v, bool) ornot isinstance(v, (int, float)) ornot math.isfinite(v):
raise ValueError(f"start and stop must be finite numbers; got {start} and {stop}")
if isinstance(count, bool) ornot isinstance(count, int) or count < 1:
raise ValueError(f"count must be a whole number of at least 1, got {count}")
raw = abs(float(stop) - float(start)) / count
if raw == 0:
return0.0, 1.0if raw >= 1:
power = 1.0while power * 10 <= raw:
power *= 10return _nice_factor(raw / power) * power, 1.0
inverse = 1.0while raw * inverse < 1:
inverse *= 10
factor = _nice_factor(raw * inverse)
return (1.0, 1.0) if factor == inverse else (1.0, inverse / factor)
def _nice_factor(error: float) -> float:
if error >= math.sqrt(50):
return10.0if error >= math.sqrt(10):
return5.0if error >= math.sqrt(2):
return2.0return1.0