Functional Weave
Code in Rust

charts.scale@1.0.0

README.md

1,745 bytes · view raw

# charts.scale

A linear scale maps a data value onto a position: `linearScale([0, 100], [0, 500], 25, false)` is `125`, a quarter of the way along a 500 pixel axis. `invertLinear` goes the other way, from a pointer position back to the value under it, and `niceDomain` widens a data extent such as `[0.13, 9.7]` to `[0, 10]` so the axis starts and ends on a tick.

This is a group: three functions that only make sense together, in one package, each in its own file. Install only what you call with `require charts.scale ^1.0.0 only=linearScale`; `invertLinear` brings `linearScale` with it, because it reuses its arithmetic.

Scales are geometry, not money, so they work in floating point. Every result is rounded to 6 decimal places inside the function (half up, as `floor(x * 1e6 + 0.5) / 1e6`), which is far below a pixel and makes TypeScript, Python and Rust agree to the digit.

A domain or range is a two-element list, `[from, to]`. Either may run backwards: a y axis usually maps `[0, max]` onto `[height, 0]`. A domain whose two ends are equal has no width to map, and `linearScale` raises rather than divide by zero; `invertLinear` raises for a range with equal ends for the same reason.

Without `clamp`, a value outside the domain maps outside the range, which is what an axis drawing a point past its end wants. With it, the result stops at the range's ends.

`niceDomain` picks a tick step of 1, 2 or 5 times a power of ten, aiming for about `count` ticks across the domain, then moves each end out to a multiple of that step, repeating until the step stops changing. It returns the step with the domain, so the axis can draw ticks on exactly those multiples. A domain whose ends are equal is returned unchanged with a step of 0.