Spaces:
Sleeping
Sleeping
| import {ticks} from "d3-array"; | |
| import {format, formatSpecifier} from "d3-format"; | |
| import nice from "./nice.js"; | |
| import {copy, transformer} from "./continuous.js"; | |
| import {initRange} from "./init.js"; | |
| function transformLog(x) { | |
| return Math.log(x); | |
| } | |
| function transformExp(x) { | |
| return Math.exp(x); | |
| } | |
| function transformLogn(x) { | |
| return -Math.log(-x); | |
| } | |
| function transformExpn(x) { | |
| return -Math.exp(-x); | |
| } | |
| function pow10(x) { | |
| return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x; | |
| } | |
| function powp(base) { | |
| return base === 10 ? pow10 | |
| : base === Math.E ? Math.exp | |
| : x => Math.pow(base, x); | |
| } | |
| function logp(base) { | |
| return base === Math.E ? Math.log | |
| : base === 10 && Math.log10 | |
| || base === 2 && Math.log2 | |
| || (base = Math.log(base), x => Math.log(x) / base); | |
| } | |
| function reflect(f) { | |
| return (x, k) => -f(-x, k); | |
| } | |
| export function loggish(transform) { | |
| const scale = transform(transformLog, transformExp); | |
| const domain = scale.domain; | |
| let base = 10; | |
| let logs; | |
| let pows; | |
| function rescale() { | |
| logs = logp(base), pows = powp(base); | |
| if (domain()[0] < 0) { | |
| logs = reflect(logs), pows = reflect(pows); | |
| transform(transformLogn, transformExpn); | |
| } else { | |
| transform(transformLog, transformExp); | |
| } | |
| return scale; | |
| } | |
| scale.base = function(_) { | |
| return arguments.length ? (base = +_, rescale()) : base; | |
| }; | |
| scale.domain = function(_) { | |
| return arguments.length ? (domain(_), rescale()) : domain(); | |
| }; | |
| scale.ticks = count => { | |
| const d = domain(); | |
| let u = d[0]; | |
| let v = d[d.length - 1]; | |
| const r = v < u; | |
| if (r) ([u, v] = [v, u]); | |
| let i = logs(u); | |
| let j = logs(v); | |
| let k; | |
| let t; | |
| const n = count == null ? 10 : +count; | |
| let z = []; | |
| if (!(base % 1) && j - i < n) { | |
| i = Math.floor(i), j = Math.ceil(j); | |
| if (u > 0) for (; i <= j; ++i) { | |
| for (k = 1; k < base; ++k) { | |
| t = i < 0 ? k / pows(-i) : k * pows(i); | |
| if (t < u) continue; | |
| if (t > v) break; | |
| z.push(t); | |
| } | |
| } else for (; i <= j; ++i) { | |
| for (k = base - 1; k >= 1; --k) { | |
| t = i > 0 ? k / pows(-i) : k * pows(i); | |
| if (t < u) continue; | |
| if (t > v) break; | |
| z.push(t); | |
| } | |
| } | |
| if (z.length * 2 < n) z = ticks(u, v, n); | |
| } else { | |
| z = ticks(i, j, Math.min(j - i, n)).map(pows); | |
| } | |
| return r ? z.reverse() : z; | |
| }; | |
| scale.tickFormat = (count, specifier) => { | |
| if (count == null) count = 10; | |
| if (specifier == null) specifier = base === 10 ? "s" : ","; | |
| if (typeof specifier !== "function") { | |
| if (!(base % 1) && (specifier = formatSpecifier(specifier)).precision == null) specifier.trim = true; | |
| specifier = format(specifier); | |
| } | |
| if (count === Infinity) return specifier; | |
| const k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate? | |
| return d => { | |
| let i = d / pows(Math.round(logs(d))); | |
| if (i * base < base - 0.5) i *= base; | |
| return i <= k ? specifier(d) : ""; | |
| }; | |
| }; | |
| scale.nice = () => { | |
| return domain(nice(domain(), { | |
| floor: x => pows(Math.floor(logs(x))), | |
| ceil: x => pows(Math.ceil(logs(x))) | |
| })); | |
| }; | |
| return scale; | |
| } | |
| export default function log() { | |
| const scale = loggish(transformer()).domain([1, 10]); | |
| scale.copy = () => copy(scale, log()).base(scale.base()); | |
| initRange.apply(scale, arguments); | |
| return scale; | |
| } | |