Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Sequence, Tuple
from .charts_scale_linear_scale import pair ← linearScale, another function of this group · built into the same file, even by a slim install
from .charts_scale_types import Band
from .math_round_float import round_float ← from math.round-float ^1.0.0 · built alongside by fune
def band_scale(
domain: Sequence[str],
range: Sequence[float],
value: str,
padding_inner: float,
padding_outer: float,
align: float,
) -> Band:
"""The band a category occupies, as d3.scaleBand lays it out (without
rounding to whole pixels). A reversed range reverses the order of the
bands, not their direction, so start is always the lower coordinate."""
if not (0 <= padding_inner <= 1):
raise ValueError(f"paddingInner must be between 0 and 1; got {padding_inner}")
check_outer(padding_outer, "paddingOuter")
check_align(align)
start, width = band_position(domain, range, value, padding_inner, padding_outer, align)
return Band(start=round_float(start, 6), center=round_float(start + width / 2, 6), width=round_float(width, 6))
# Exported for point_scale, which is a band scale whose bands have no width.
def check_outer(padding: float, what: str) -> None:
if not (padding >= 0):
raise ValueError(f"{what} must be 0 or more; got {padding}")
def check_align(align: float) -> None:
if not (0 <= align <= 1):
raise ValueError(f"align must be between 0 and 1; got {align}")
def band_position(
domain: Sequence[str],
extent: Sequence[float],
value: str,
padding_inner: float,
padding_outer: float,
align: float,
) -> Tuple[float, float]:
"""The unrounded lower coordinate of value's band, and the band width."""
r0, r1 = pair(extent, "range")
n = len(domain)
if n == 0:
raise ValueError("domain must not be empty")
index = -1
for i in range(n):
for j in range(i):
if domain[j] == domain[i]:
raise ValueError(f'domain has a repeated value: "{domain[i]}"')
if domain[i] == value:
index = i
if index < 0:
raise ValueError(f'value is not in the domain: "{value}"')
reverse = r1 < r0
start = r1 if reverse else r0
stop = r0 if reverse else r1
step = (stop - start) / max(1.0, n - float(padding_inner) + float(padding_outer) * 2)
start += (stop - start - step * (n - float(padding_inner))) * float(align)
slot = n - 1 - index if reverse else index
return start + step * slot, step * (1 - float(padding_inner))